├── .gitignore ├── README.md ├── icons ├── addInfluencer.png ├── addLattice.png ├── addObject.png ├── createInfluencer.png ├── deleteInfluencer.png ├── deleteLattice.png ├── deleteObject.png ├── maya_icon.png └── refreshIcon.png ├── include ├── cameraLattice.h ├── cameraLatticeInfluenceLocator.h └── cameraLatticeTranslator.h ├── python └── tcCameraLattice │ ├── __init__.py │ ├── icons │ ├── abouts.png │ ├── addInfluencer.png │ ├── addLattice.png │ ├── addObject.png │ ├── createInfluencer.png │ ├── deleteInfluencer.png │ ├── deleteLattice.png │ ├── deleteObject.png │ └── refreshIcon.png │ └── tcCameraLattice.py └── source ├── PluginMain.cpp ├── cameraLattice.cpp ├── cameraLatticeInfluenceLocator.cpp └── cameraLatticeTranslator.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.sdf 2 | *.opensdf 3 | *.pyc 4 | .project 5 | .cproject 6 | x64/ 7 | *.o 8 | Debug/ 9 | build/ 10 | .project 11 | .cproject 12 | *.exe 13 | *.msi 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Maya Camera Lattice 2 | 3 | Our Camera Lattice tool is the ultimate solution for mesh deformations in camera space. Attach a lattice to your camera and deform any mesh in a matter of seconds! 4 | 5 | ### Features: 6 | 7 | * Lattice deformation in camera space 8 | * Multiple lattices per camera 9 | * Easily affects multiple meshes with a single camera lattice 10 | * Bezier and linear interpolations 11 | * Recursion parameter for bezier deformation 12 | * Deform meshes with multiple camera lattices 13 | * Utilities for accurate control on lattice points 14 | * Gate Offset attribute to prevent weird deformations on lattice edges 15 | * Influence Areas locators to localise deformation in 3D space 16 | 17 | If you are planning to use one of our tools in a studio, we would be grateful if you could let us know. 18 | 19 | ### Known limitations: 20 | 21 | * Few free maya rigs you find on the internet may not work with this tool 22 | * The camera lattice is a poly plane; you cannot select objects beyond it in object selection mode when looking through camera panel. 23 | * You can only have one camera lattice per camera. 24 | 25 | ### Maya Bugs 26 | * Viewport 2.0 does not display the lattice correctly when the camera near clip plane is different from the default value. Please use the “Legacy Default Viewport” when using this tool. 27 | * Manipulators may not be displayed correctly in camera view 28 | 29 | ## License 30 | 31 | This project is licensed under [the LGPL license](http://www.gnu.org/licenses/). 32 | 33 | ## Contact us 34 | 35 | Please feel free to contact us at support@toolchefs.com in case you would like contribute or simply have questions. 36 | 37 | ### ENJOY! 38 | -------------------------------------------------------------------------------- /icons/addInfluencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/addInfluencer.png -------------------------------------------------------------------------------- /icons/addLattice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/addLattice.png -------------------------------------------------------------------------------- /icons/addObject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/addObject.png -------------------------------------------------------------------------------- /icons/createInfluencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/createInfluencer.png -------------------------------------------------------------------------------- /icons/deleteInfluencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/deleteInfluencer.png -------------------------------------------------------------------------------- /icons/deleteLattice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/deleteLattice.png -------------------------------------------------------------------------------- /icons/deleteObject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/deleteObject.png -------------------------------------------------------------------------------- /icons/maya_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/maya_icon.png -------------------------------------------------------------------------------- /icons/refreshIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/icons/refreshIcon.png -------------------------------------------------------------------------------- /include/cameraLattice.h: -------------------------------------------------------------------------------- 1 | // 2 | // cameraLattice.h 3 | // cameraLattice 4 | // 5 | // Created by Daniele Federico on 14/12/14. 6 | // 7 | // 8 | 9 | #ifndef CAMERA_LATTICE_H 10 | #define CAMERA_LATTICE_H 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | struct Influencer 26 | { 27 | MMatrix invMat; 28 | MVector pos; 29 | double falloff; 30 | double maxAxisLength; 31 | }; 32 | 33 | class CameraLatticeData 34 | { 35 | public: 36 | 37 | struct ThreadData 38 | { 39 | MMatrix *projectionMatrix; 40 | MMatrix *invProjectionMatrix; 41 | MMatrix *toWorldMatrix; 42 | MPointArray *points; 43 | MPointArray *deformedPoints; 44 | 45 | MPointArray *planePoints; 46 | 47 | double filmHAperture, filmVAperture; 48 | int sD, tD; 49 | int maxRecursion, behaviour; 50 | double gateOffsetValue; 51 | 52 | std::vector *influencers; 53 | 54 | double envelopeValue; 55 | 56 | bool isOrtho; 57 | 58 | }; 59 | 60 | ~CameraLatticeData(){}; 61 | 62 | CameraLatticeData(MMatrix *projectionMatrix, 63 | MMatrix *invProjectionMatrix, 64 | MMatrix *toWorldMatrix, 65 | MPointArray *points, 66 | MPointArray *deformedPoints, 67 | MPointArray* planePoints, 68 | double filmHAperture, double filmVAperture, 69 | int sD, int tD, bool isOrtho, int maxRecursion, int behaviour, 70 | std::vector *influencers, double gateOffsetValue, double envelopeValue) 71 | { 72 | m_data.projectionMatrix = projectionMatrix; 73 | m_data.invProjectionMatrix = invProjectionMatrix; 74 | m_data.points = points; 75 | m_data.deformedPoints = deformedPoints; 76 | m_data.planePoints = planePoints; 77 | m_data.filmHAperture = filmHAperture; 78 | m_data.filmVAperture = filmVAperture; 79 | m_data.sD = sD; 80 | m_data.tD = tD; 81 | m_data.isOrtho = isOrtho; 82 | m_data.maxRecursion = maxRecursion; 83 | m_data.behaviour = behaviour; 84 | m_data.influencers = influencers; 85 | m_data.toWorldMatrix = toWorldMatrix; 86 | m_data.gateOffsetValue = gateOffsetValue; 87 | m_data.envelopeValue = envelopeValue; 88 | } 89 | 90 | void operator()( const tbb::blocked_range& r ) const; 91 | 92 | private: 93 | 94 | struct ThreadData m_data; 95 | }; 96 | 97 | class CameraLattice : public MPxDeformerNode 98 | { 99 | public: 100 | CameraLattice(); 101 | virtual ~CameraLattice(); 102 | 103 | static void* creator(); 104 | static MStatus initialize(); 105 | 106 | // deformation function 107 | // 108 | virtual MStatus deform(MDataBlock& block, 109 | MItGeometry& iter, 110 | const MMatrix& mat, 111 | unsigned int multiIndex); 112 | 113 | virtual MStatus connectionMade (const MPlug &plug, const MPlug &otherPlug, bool asSrc); 114 | virtual MStatus connectionBroken (const MPlug &plug, const MPlug &otherPlug, bool asSrc); 115 | 116 | 117 | public: 118 | // local node attributes 119 | 120 | static MObject inputLattice; 121 | static MObject interpolation; 122 | static MObject deformerMessage; 123 | static MObject latticeToDeformerMessage; 124 | static MObject sSubidivision; 125 | static MObject tSubidivision; 126 | static MObject objectMatrix; 127 | static MObject cameraMatrix; 128 | static MObject inOrtho; 129 | static MObject inOrthographicWidth; 130 | static MObject inVerticalFilmAperture; 131 | static MObject inHorizontalFilmAperture; 132 | static MObject inFocalLength; 133 | static MObject maxBezierRecursion; 134 | static MObject influenceFalloff; 135 | static MObject influenceMatrix; 136 | static MObject gateOffset; 137 | 138 | static MTypeId id; 139 | 140 | private: 141 | bool refreshLogicalIndex; 142 | MIntArray cachedLogicalIndex; 143 | 144 | }; 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /include/cameraLatticeInfluenceLocator.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef CAMERA_LATTICE_INFLUENCE_LOCATOR_H 4 | #define CAMERA_LATTICE_INFLUENCE_LOCATOR_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // Viewport 2.0 includes 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | class CameraLatticeInfluenceLocator : public MPxLocatorNode 28 | { 29 | public: 30 | CameraLatticeInfluenceLocator(); 31 | virtual ~CameraLatticeInfluenceLocator(); 32 | 33 | virtual MStatus compute( const MPlug& plug, MDataBlock& data ); 34 | 35 | #if MAYA_API_VERSION < 201900 // obsolete for Maya 2019 version and higher 36 | virtual void draw( M3dView & view, const MDagPath & path, 37 | M3dView::DisplayStyle style, 38 | M3dView::DisplayStatus status ); 39 | #endif 40 | 41 | virtual bool isBounded() const; 42 | virtual MBoundingBox boundingBox() const; 43 | 44 | static void * creator(); 45 | static MStatus initialize(); 46 | 47 | void drawCircle(const int axis, const double radius); 48 | 49 | // the falloff of the influencer 50 | static MObject falloff; 51 | static MObject message; 52 | 53 | public: 54 | static MTypeId id; 55 | static MString drawDbClassification; 56 | static MString drawRegistrantId; 57 | }; 58 | 59 | class CameraLatticeInfluenceData : public MUserData 60 | { 61 | public: 62 | CameraLatticeInfluenceData() : MUserData(false) {} // don't delete after draw 63 | virtual ~CameraLatticeInfluenceData() {} 64 | 65 | MColor color; 66 | MPoint center; 67 | MVector X, Y, Z; 68 | double falloffVal; 69 | }; 70 | 71 | class CameraLatticeInfluenceDrawOverride : public MHWRender::MPxDrawOverride 72 | { 73 | public: 74 | static MHWRender::MPxDrawOverride* Creator(const MObject& obj) 75 | { 76 | return new CameraLatticeInfluenceDrawOverride(obj); 77 | } 78 | 79 | virtual ~CameraLatticeInfluenceDrawOverride(); 80 | 81 | virtual MHWRender::DrawAPI supportedDrawAPIs() const; 82 | 83 | virtual bool isBounded( 84 | const MDagPath& objPath, 85 | const MDagPath& cameraPath) const; 86 | 87 | virtual bool disableInternalBoundingBoxDraw() const; 88 | 89 | virtual MBoundingBox boundingBox( 90 | const MDagPath& objPath, 91 | const MDagPath& cameraPath) const; 92 | 93 | virtual MUserData* prepareForDraw( 94 | const MDagPath& objPath, 95 | const MDagPath& cameraPath, 96 | const MHWRender::MFrameContext& frameContext, 97 | MUserData* oldData); 98 | 99 | virtual bool hasUIDrawables() const { return true; } 100 | 101 | virtual void addUIDrawables( 102 | const MDagPath& objPath, 103 | MHWRender::MUIDrawManager& drawManager, 104 | const MHWRender::MFrameContext& frameContext, 105 | const MUserData* data); 106 | 107 | static void draw(const MHWRender::MDrawContext& context, const MUserData* data) {}; 108 | 109 | protected: 110 | MBoundingBox mCurrentBoundingBox; 111 | MCallbackId fModelEditorChangedCbId; 112 | MObject cameraLatticeInfluenceLocator; 113 | private: 114 | CameraLatticeInfluenceDrawOverride(const MObject& obj); 115 | float getMultiplier(const MDagPath& objPath) const; 116 | 117 | static void OnModelEditorChanged(void *clientData); 118 | }; 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /include/cameraLatticeTranslator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CameraLatticeTranslator.h 3 | * CameraLatticeTranslator 4 | * 5 | * 6 | */ 7 | 8 | #ifndef CAMERA_LATTICE_TRANSLATOR_H 9 | #define CAMERA_LATTICE_TRANSLATOR_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | class CameraLatticeTranslator : public MPxNode 22 | { 23 | public: 24 | static void *creator(); 25 | static MStatus initialize(); 26 | 27 | virtual MStatus compute( const MPlug& plug, MDataBlock& data ); 28 | 29 | static MTypeId id; 30 | 31 | 32 | private: 33 | static MObject inNearClipPlane; 34 | static MObject inFocalLength; 35 | static MObject inHorizontalFilmAperture; 36 | static MObject inVerticalFilmAperture; 37 | static MObject inOrtho; 38 | static MObject inOrthographicWidth; 39 | static MObject outScaleX; 40 | static MObject outScaleY; 41 | static MObject outTranslateZ; 42 | 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /python/tcCameraLattice/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/__init__.py -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/abouts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/abouts.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/addInfluencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/addInfluencer.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/addLattice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/addLattice.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/addObject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/addObject.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/createInfluencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/createInfluencer.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/deleteInfluencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/deleteInfluencer.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/deleteLattice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/deleteLattice.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/deleteObject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/deleteObject.png -------------------------------------------------------------------------------- /python/tcCameraLattice/icons/refreshIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Toolchefs/cameraLattice/474be4831f617b7c4f4aa02ff2ee0b12bef1342b/python/tcCameraLattice/icons/refreshIcon.png -------------------------------------------------------------------------------- /python/tcCameraLattice/tcCameraLattice.py: -------------------------------------------------------------------------------- 1 | # Toolchefs ltd - Software Disclaimer 2 | # 3 | # Copyright 2014 Toolchefs Limited 4 | # 5 | # The software, information, code, data and other materials (Software) 6 | # contained in, or related to, these files is the confidential and proprietary 7 | # information of Toolchefs ltd. 8 | # The software is protected by copyright. The Software must not be disclosed, 9 | # distributed or provided to any third party without the prior written 10 | # authorisation of Toolchefs ltd. 11 | 12 | 13 | import os 14 | import re 15 | import sys 16 | import traceback 17 | 18 | try: 19 | from PySide import QtGui, QtCore 20 | import PySide.QtGui as QtWidgets 21 | import shiboken 22 | except ImportError: 23 | from PySide2 import QtGui, QtCore, QtWidgets 24 | import shiboken2 as shiboken 25 | 26 | from maya import OpenMaya 27 | from maya import cmds 28 | from __builtin__ import False 29 | 30 | LATTICE_MAYA_TYPE = 'mesh' 31 | CAMERA_MAYA_TYPE = 'camera' 32 | LATTICE_MESSAGE_ATTRIBUTE = 'camera' 33 | DEFORMER_MESSAGE_ATTRIBUTE = 'deformerMessage' 34 | LATTICE_TO_DEFORMER_MESSAGE_ATTRIBUTE = 'ldMessage' 35 | INFLUENCE_MESSAGE_ATTRIBUTE = 'locatorMessage' 36 | CAMERA_LATTICE_BASE_NAME = 'cameraLattice' 37 | CAMERA_LATTICE_DEFORMER = 'tcCameraLatticeDeformer' 38 | CAMERA_LATTICE_INFLUENCER = 'tcCameraLatticeInfluenceAreaLocator' 39 | 40 | CAMERA_LATTICE_PARENT_ATTR = 'cameraLatticeParentAttr' 41 | LATTICE_ACTIVE_ATTR = 'lActive' 42 | INTERPOLATION_ATTR = 'interpolation' 43 | SDIVISIONS_ATTR = 'sDivisions' 44 | TDIVISIONS_ATTR = 'tDivisions' 45 | MAX_BEZIER_RECURSION_ATTR = 'maxRecursion' 46 | GATE_OFFSET_ATTR = 'gateOffset' 47 | 48 | def _is_deformable(obj): 49 | if cmds.nodeType(obj) == "transform": 50 | shapes = cmds.listRelatives(obj, shapes=True) 51 | if not shapes: 52 | return False 53 | return bool(cmds.ls(shapes[0], type="deformableShape")) 54 | 55 | return bool(cmds.ls(obj, type="deformableShape")) 56 | 57 | def _get_camera_shape(camera): 58 | if cmds.nodeType(camera) == CAMERA_MAYA_TYPE: 59 | return camera 60 | shapes = cmds.listRelatives(camera, shapes=True) 61 | if not shapes: 62 | return None 63 | if cmds.nodeType(shapes[0]) == CAMERA_MAYA_TYPE: 64 | return shapes[0] 65 | 66 | def _is_lattice(item): 67 | if cmds.nodeType(item) == LATTICE_MAYA_TYPE: 68 | item = cmds.listRelatives(item, fullPath=True, parent=True)[0] 69 | return cmds.nodeType(item) == "transform" and cmds.objExists(item + '.' + CAMERA_LATTICE_PARENT_ATTR) 70 | 71 | def _is_camera(item): 72 | return bool(_get_camera_shape(item)) 73 | 74 | def _get_unique_camera_lattice_name(): 75 | counter = 1 76 | while cmds.objExists(CAMERA_LATTICE_BASE_NAME + str(counter)): 77 | counter += 1 78 | return CAMERA_LATTICE_BASE_NAME + str(counter) 79 | 80 | def _get_connected_items(plug_name, with_plug=False, destination=False, types=[]): 81 | items = [] 82 | selList = OpenMaya.MSelectionList() 83 | selList.add(plug_name) 84 | 85 | mPlug = OpenMaya.MPlug() 86 | selList.getPlug(0, mPlug) 87 | 88 | mPlugArray = OpenMaya.MPlugArray() 89 | if mPlug.isArray(): 90 | num = mPlug.numConnectedElements() 91 | thisPlugArray = OpenMaya.MPlugArray() 92 | for i in range(num): 93 | p = mPlug.connectionByPhysicalIndex(i) 94 | p.connectedTo(thisPlugArray, not destination, destination) 95 | for j in range(thisPlugArray.length()): 96 | mPlugArray.append(thisPlugArray[j]) 97 | else: 98 | mPlug.connectedTo(mPlugArray, not destination, destination) 99 | 100 | dp = OpenMaya.MDagPath() 101 | for index in range(mPlugArray.length()): 102 | try: 103 | OpenMaya.MDagPath.getAPathTo(mPlugArray[index].node(), dp) 104 | if with_plug: 105 | items.append(dp.fullPathName() + '.' + mPlugArray[index].partialName(False, False, False, False, False, True)) 106 | else: 107 | if not types or (types and cmds.nodeType(dp.fullPathName()) in types): 108 | items.append(dp.fullPathName()) 109 | except RuntimeError: 110 | depFn = OpenMaya.MFnDependencyNode(mPlugArray[index].node()) 111 | if with_plug: 112 | items.append(depFn.name() + '.' + mPlugArray[index].partialName(False, False, False, False, False, True)) 113 | else: 114 | if not types or (types and cmds.nodeType(depFn.name()) in types): 115 | items.append(depFn.name()) 116 | return items 117 | 118 | def _get_transform_from_camera_shape(item): 119 | if cmds.nodeType(item) == CAMERA_MAYA_TYPE: 120 | return cmds.listRelatives(item, fullPath=True, parent=True)[0] 121 | return item 122 | 123 | def _get_camera(selection): 124 | for s in selection: 125 | if _is_camera(s): 126 | return _get_transform_from_camera_shape(s) 127 | elif _is_lattice(s) and cmds.objExists(s + '.' + CAMERA_MAYA_TYPE): 128 | result = _get_connected_items(s + '.' + CAMERA_MAYA_TYPE) 129 | if result and _is_camera(result[0]): 130 | return _get_transform_from_camera_shape(result[0]) 131 | 132 | def _get_lattices_from_camera(camera): 133 | shape = _get_camera_shape(camera) 134 | messages = _get_connected_items(shape + '.message', with_plug=True, destination=True) 135 | if messages is None: 136 | return [] 137 | 138 | lattices = [] 139 | for m in messages: 140 | tokens = m.split('.') 141 | if tokens[1] == LATTICE_MESSAGE_ATTRIBUTE and _is_lattice(tokens[0]): 142 | lattices.append(tokens[0]) 143 | return lattices 144 | 145 | def _delete_lattice_deformers(lattice): 146 | deformers = _get_connected_items(lattice + '.message', destination=True, types=[CAMERA_LATTICE_DEFORMER]) 147 | if deformers: 148 | cmds.delete(deformers) 149 | 150 | def _get_selected_influencers(): 151 | selection = cmds.ls(sl=True, l=True) 152 | influencers = [] 153 | for s in selection: 154 | if cmds.nodeType(s)==CAMERA_LATTICE_INFLUENCER: 155 | influencers.append(s) 156 | continue 157 | 158 | shapes = cmds.listRelatives(s, shapes=True) 159 | if not shapes: 160 | continue 161 | 162 | if cmds.nodeType(shapes[0])==CAMERA_LATTICE_INFLUENCER: 163 | influencers.append(s) 164 | 165 | return influencers 166 | 167 | def get_connected_index_attr(attr, array_attr): 168 | indices = cmds.getAttr(array_attr, mi=True) 169 | if not indices: 170 | return -1 171 | for index in indices: 172 | if cmds.isConnected(attr, array_attr + "[%d]" % index): 173 | return index 174 | return -1 175 | 176 | def _get_next_index_for_attribute_array(attribute): 177 | indices = cmds.getAttr(attribute, mi=True) 178 | if indices is not None: 179 | indices.sort() 180 | return indices[-1]+1 181 | else: 182 | return 0 183 | 184 | def _apply_influence_area_to_deformer(deformer, area): 185 | index = get_connected_index_attr(area + ".falloff", deformer + ".influenceFalloff") 186 | if index != -1: 187 | cmds.warning("tcCameraLattice: influence area already applied to " + deformer) 188 | return 189 | 190 | index = _get_next_index_for_attribute_array(deformer + '.influenceMatrix') 191 | cmds.connectAttr(area + '.worldMatrix[0]', deformer + '.influenceMatrix[%d]' % index) 192 | cmds.connectAttr(area + '.falloff', deformer + '.influenceFalloff[%d]' % index) 193 | 194 | def _apply_influence_area_to_lattice(lattice, area): 195 | if get_connected_index_attr(lattice + ".message", area + "." + INFLUENCE_MESSAGE_ATTRIBUTE) != -1: 196 | return False 197 | 198 | index = _get_next_index_for_attribute_array(area + "." + INFLUENCE_MESSAGE_ATTRIBUTE) 199 | cmds.connectAttr(lattice + ".message", area + ".%s[%d]" % (INFLUENCE_MESSAGE_ATTRIBUTE, index)) 200 | 201 | deformers = _get_connected_items(lattice + '.message', destination=True, types=[CAMERA_LATTICE_DEFORMER]) 202 | if not deformers: 203 | return True 204 | 205 | for d in deformers: 206 | _apply_influence_area_to_deformer(d, area) 207 | 208 | return True 209 | 210 | def _create_influence_area(lattice): 211 | node = cmds.createNode(CAMERA_LATTICE_INFLUENCER, n=CAMERA_LATTICE_INFLUENCER+"Shape") 212 | 213 | camera = _get_camera([lattice]) 214 | matrix = cmds.getAttr(camera+".worldMatrix[0]") 215 | 216 | transform = cmds.listRelatives(node, fullPath=True, parent=True)[0] 217 | cmds.addAttr(transform, ln="falloff", at="double", minValue=0, maxValue=1, defaultValue=0.5, keyable=True) 218 | cmds.setAttr(transform+".falloff", channelBox=True) 219 | cmds.connectAttr(transform+".falloff", node+".falloff") 220 | 221 | transform = cmds.rename(transform, CAMERA_LATTICE_INFLUENCER) 222 | 223 | x = matrix[12] + matrix[8] * -3 224 | y = matrix[13] + matrix[9] * -3 225 | z = matrix[14] + matrix[10] * -3 226 | cmds.setAttr(transform+'.tx', x) 227 | cmds.setAttr(transform+'.ty', y) 228 | cmds.setAttr(transform+'.tz', z) 229 | 230 | _apply_influence_area_to_lattice(lattice, transform) 231 | 232 | cmds.select(transform, r=True) 233 | return transform 234 | 235 | def _get_all_influencers(lattice): 236 | shapes = _get_connected_items(lattice + '.message', destination=True, types=[CAMERA_LATTICE_INFLUENCER]) 237 | return [cmds.listRelatives(node, fullPath=True, parent=True)[0] for node in shapes] 238 | 239 | def _get_infuencer_full_path(lattice, influencer): 240 | influencers = _get_all_influencers(lattice) 241 | for i in influencers: 242 | if i.endswith(influencer): 243 | return i 244 | 245 | def _disconnect_influencers(lattice, influencers): 246 | for i in influencers: 247 | fi = _get_infuencer_full_path(lattice, i) 248 | if not fi: 249 | continue 250 | 251 | index = get_connected_index_attr(lattice + ".message", fi + "." + INFLUENCE_MESSAGE_ATTRIBUTE) 252 | if index == -1: 253 | continue 254 | cmds.disconnectAttr(lattice + ".message", fi + ".%s[%d]" % (INFLUENCE_MESSAGE_ATTRIBUTE, index)) 255 | 256 | deformers = _get_connected_items(lattice + '.message', destination=True, types=[CAMERA_LATTICE_DEFORMER]) 257 | for d in deformers: 258 | index = get_connected_index_attr(fi + ".falloff", d + ".influenceFalloff") 259 | if index == -1: 260 | cmds.warning("tcCameraLattice: something is wrong with your influece are to deformer connections.") 261 | continue 262 | 263 | cmds.disconnectAttr(fi + ".falloff", d + ".influenceFalloff[%d]" % index) 264 | cmds.disconnectAttr(fi + ".worldMatrix[0]", d + ".influenceMatrix[%d]" % index) 265 | 266 | def _finalise_attribute(attribute): 267 | cmds.setAttr(attribute, l=True) 268 | cmds.setAttr(attribute, k=False) 269 | cmds.setAttr(attribute, channelBox=False) 270 | 271 | def _finalise_lattice(lattice, x_div, y_div): 272 | cmds.setAttr(lattice + '.sz', 0) 273 | 274 | _finalise_attribute(lattice + '.tx') 275 | _finalise_attribute(lattice + '.ty') 276 | _finalise_attribute(lattice + '.rx') 277 | _finalise_attribute(lattice + '.ry') 278 | _finalise_attribute(lattice + '.rz') 279 | _finalise_attribute(lattice + '.sz') 280 | 281 | _finalise_attribute(lattice + '.sx') 282 | _finalise_attribute(lattice + '.sy') 283 | _finalise_attribute(lattice + '.tz') 284 | 285 | node = cmds.createNode('addDoubleLinear') 286 | _finalise_attribute(node + '.input1') 287 | _finalise_attribute(node + '.input2') 288 | for i in range(x_div * y_div): 289 | cmds.connectAttr(node + ".output", lattice + ".pt[" + str(i) + "].pz") 290 | 291 | def _create_camera_lattice(camera, x_div, y_div): 292 | selection = cmds.ls(sl=True, l=True) 293 | 294 | cmds.select(cl=True) 295 | plane_objects = cmds.polyPlane(w=1, h=1, sx=x_div - 1, sy=y_div - 1, ax=[0, 0, 1], cuv=0, ch=1) 296 | cmds.delete(plane_objects[0], ch=True) 297 | lattice = plane_objects[0] 298 | 299 | cmds.setAttr(lattice + ".overrideEnabled", 1) 300 | cmds.setAttr(lattice + ".overrideShading", 0) 301 | 302 | max_div = x_div if x_div > y_div else y_div 303 | 304 | cmds.addAttr(lattice, ln=CAMERA_LATTICE_PARENT_ATTR, numberOfChildren=7, attributeType='compound') 305 | cmds.addAttr(lattice, ln=LATTICE_ACTIVE_ATTR, at="double", parent=CAMERA_LATTICE_PARENT_ATTR, maxValue=1, minValue=0, defaultValue=1) 306 | cmds.addAttr(lattice, ln=LATTICE_MESSAGE_ATTRIBUTE, at="message", parent=CAMERA_LATTICE_PARENT_ATTR) 307 | cmds.addAttr(lattice, ln=INTERPOLATION_ATTR, at='enum', enumName='linear:bezier', parent=CAMERA_LATTICE_PARENT_ATTR) 308 | cmds.addAttr(lattice, ln=SDIVISIONS_ATTR, at="long", parent=CAMERA_LATTICE_PARENT_ATTR, minValue=3) 309 | cmds.addAttr(lattice, ln=TDIVISIONS_ATTR, at="long", parent=CAMERA_LATTICE_PARENT_ATTR, minValue=3) 310 | cmds.addAttr(lattice, ln=MAX_BEZIER_RECURSION_ATTR, at="long", parent=CAMERA_LATTICE_PARENT_ATTR, maxValue=max_div - 2, minValue=1, keyable=True) 311 | cmds.addAttr(lattice, ln=GATE_OFFSET_ATTR, at="double", parent=CAMERA_LATTICE_PARENT_ATTR, maxValue=1, minValue=0, defaultValue=0.1, keyable=True) 312 | 313 | cmds.setAttr(lattice + "." + SDIVISIONS_ATTR, x_div) 314 | cmds.setAttr(lattice + "." + TDIVISIONS_ATTR, y_div) 315 | cmds.setAttr(lattice + "." + INTERPOLATION_ATTR, channelBox=True) 316 | cmds.setAttr(lattice + "." + MAX_BEZIER_RECURSION_ATTR, 4 if max_div / 2 > 4 else max_div / 2) 317 | cmds.setAttr(lattice + "." + LATTICE_ACTIVE_ATTR, channelBox=False) 318 | 319 | lattice = cmds.rename(lattice, _get_unique_camera_lattice_name()) 320 | cmds.parent(lattice, camera) 321 | cmds.setAttr(lattice + '.tx', 0) 322 | cmds.setAttr(lattice + '.ty', 0) 323 | cmds.setAttr(lattice + '.tz', 0) 324 | cmds.setAttr(lattice + '.rx', 0) 325 | cmds.setAttr(lattice + '.ry', 0) 326 | cmds.setAttr(lattice + '.rz', 0) 327 | 328 | converter = cmds.createNode('tcCameraLatticeTranslator') 329 | cmds.connectAttr(camera + '.nearClipPlane', converter + '.inNearClipPlane') 330 | cmds.connectAttr(camera + '.focalLength', converter + '.inFocalLength') 331 | cmds.connectAttr(camera + '.horizontalFilmAperture', converter + '.inHorizontalFilmAperture') 332 | cmds.connectAttr(camera + '.verticalFilmAperture', converter + '.inVerticalFilmAperture') 333 | cmds.connectAttr(camera + '.orthographic', converter + '.inOrtho') 334 | cmds.connectAttr(camera + '.orthographicWidth', converter + '.inOrthographicWidth') 335 | cmds.connectAttr(converter + '.outScaleX', lattice + '.scaleX') 336 | cmds.connectAttr(converter + '.outScaleY', lattice + '.scaleY') 337 | cmds.connectAttr(converter + '.outTranslateZ', lattice + '.translateZ') 338 | 339 | camera_shape = cmds.listRelatives(camera, shapes=True)[0] 340 | cmds.connectAttr(camera_shape + '.message', lattice + '.' + LATTICE_MESSAGE_ATTRIBUTE) 341 | 342 | _finalise_lattice(lattice, x_div, y_div) 343 | 344 | cmds.select(lattice, r=True) 345 | 346 | return cmds.ls(lattice, l=True)[0] 347 | 348 | def _get_all_affected_objects(lattice): 349 | cameraLatticeDeformers = _get_connected_items(lattice + '.message', destination=True, types=[CAMERA_LATTICE_DEFORMER]) 350 | #get all objects connected 351 | objects = {} 352 | for cld in cameraLatticeDeformers: 353 | obj = _get_connected_items(cld + '.' + DEFORMER_MESSAGE_ATTRIBUTE, destination=False) 354 | if obj: 355 | objects[cld] = obj[0] 356 | return objects 357 | 358 | def _apply_camera_lattice(object, lattice): 359 | messages = _get_connected_items(lattice + '.' + LATTICE_MESSAGE_ATTRIBUTE, with_plug=False, destination=False) 360 | if not messages or len(messages) > 1: 361 | raise RuntimeError('Camera Lattice: could not find camera shape from lattice.') 362 | 363 | deformer = str(cmds.deformer(object, type=CAMERA_LATTICE_DEFORMER)[0]) 364 | if not deformer: 365 | raise RuntimeError('Camera Lattice: could not create ' + CAMERA_LATTICE_DEFORMER + '.') 366 | 367 | cmds.connectAttr(lattice + '.outMesh', deformer + '.il') 368 | cmds.connectAttr(lattice + '.' + INTERPOLATION_ATTR, deformer + '.i') 369 | cmds.connectAttr(lattice + '.' + SDIVISIONS_ATTR, deformer + '.ss') 370 | cmds.connectAttr(lattice + '.' + TDIVISIONS_ATTR, deformer + '.ts') 371 | cmds.connectAttr(lattice + '.' + MAX_BEZIER_RECURSION_ATTR, deformer + '.mbr') 372 | cmds.connectAttr(lattice + '.message', deformer + "." + LATTICE_TO_DEFORMER_MESSAGE_ATTRIBUTE) 373 | cmds.connectAttr(lattice + '.' + LATTICE_ACTIVE_ATTR, deformer + '.envelope') 374 | cmds.connectAttr(lattice + '.' + GATE_OFFSET_ATTR, deformer + '.gateOffset') 375 | 376 | cmds.connectAttr(object + ".worldMatrix[0]", deformer + '.om') 377 | cmds.connectAttr(str(messages[0]) + ".worldMatrix[0]", deformer + '.cm') 378 | cmds.connectAttr(str(messages[0]) + ".focalLength", deformer + '.iFL') 379 | cmds.connectAttr(str(messages[0]) + ".horizontalFilmAperture", deformer + '.iHF') 380 | cmds.connectAttr(str(messages[0]) + ".verticalFilmAperture", deformer + '.iVF') 381 | cmds.connectAttr(str(messages[0]) + ".orthographicWidth", deformer + '.iOW') 382 | cmds.connectAttr(str(messages[0]) + ".orthographic", deformer + '.iO') 383 | 384 | cmds.connectAttr(object + ".message", deformer + "." + DEFORMER_MESSAGE_ATTRIBUTE) 385 | 386 | for influencer in _get_all_influencers(lattice): 387 | _apply_influence_area_to_deformer(deformer, influencer) 388 | 389 | return deformer 390 | 391 | 392 | ########################## 393 | ######GUI################# 394 | ########################## 395 | 396 | def _build_layout(horizontal): 397 | if (horizontal): 398 | layout = QtWidgets.QHBoxLayout() 399 | else: 400 | layout = QtWidgets.QVBoxLayout() 401 | layout.setContentsMargins(QtCore.QMargins(2, 2, 2, 2)) 402 | return layout 403 | 404 | 405 | def _create_separator(vertical=False): 406 | separator = QtWidgets.QFrame() 407 | if vertical: 408 | separator.setFrameShape(QtWidgets.QFrame.VLine) 409 | else: 410 | separator.setFrameShape(QtWidgets.QFrame.HLine) 411 | separator.setFrameShadow(QtWidgets.QFrame.Sunken) 412 | return separator 413 | 414 | 415 | def _get_icons_path(): 416 | tokens = __file__.split(os.path.sep) 417 | return os.path.sep.join(tokens[:-1] + ['icons']) + os.path.sep 418 | 419 | 420 | class LineWidget(QtWidgets.QWidget): 421 | def __init__(self, label, widget, width=90, parent=None): 422 | super(LineWidget, self).__init__(parent=parent) 423 | 424 | self._main_layout = _build_layout(True) 425 | self.setLayout(self._main_layout) 426 | 427 | label = QtWidgets.QLabel(label) 428 | label.setFixedWidth(width) 429 | self._main_layout.addWidget(label) 430 | self._main_layout.addWidget(widget) 431 | 432 | class CameraLatticeCreateDialog(QtWidgets.QDialog): 433 | def __init__(self, parent=None): 434 | super(CameraLatticeCreateDialog, self).__init__(parent) 435 | self.setWindowTitle("Camera Lattice Dialog") 436 | self.setObjectName("Camera Lattice Dialog") 437 | 438 | self.setFixedSize(400, 130) 439 | 440 | self._main_layout = _build_layout(False) 441 | self.setLayout(self._main_layout) 442 | 443 | self._x_div = QtWidgets.QSpinBox() 444 | self._x_div.setSingleStep(1) 445 | self._x_div.setMinimum(3) 446 | self._x_div.setMaximum(100) 447 | self._x_div.setValue(10) 448 | self._main_layout.addWidget(LineWidget("X Division:", self._x_div)) 449 | 450 | self._y_div = QtWidgets.QSpinBox() 451 | self._y_div.setSingleStep(1) 452 | self._y_div.setMinimum(3) 453 | self._y_div.setMaximum(100) 454 | self._y_div.setValue(10) 455 | self._main_layout.addWidget(LineWidget("Y Division:", self._y_div)) 456 | 457 | # OK and Cancel buttons 458 | self._buttons = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, self) 459 | self._main_layout.addWidget(self._buttons) 460 | 461 | self._buttons.accepted.connect(self.accept) 462 | self._buttons.rejected.connect(self.reject) 463 | 464 | def get_divs(self): 465 | return (self._x_div.value(), self._y_div.value()) 466 | 467 | @staticmethod 468 | def get_result(parent=None): 469 | dialog = CameraLatticeCreateDialog(parent) 470 | result = dialog.exec_() 471 | divs = dialog.get_divs() 472 | return (divs[0], divs[1], result == QtWidgets.QDialog.Accepted) 473 | 474 | 475 | class CameraLatticeTreeWidgetItem(QtWidgets.QTreeWidgetItem): 476 | def __init__(self): 477 | super(CameraLatticeTreeWidgetItem, self).__init__(QtWidgets.QTreeWidgetItem.UserType + 1) 478 | self.camera_lattice_deformer_node = None 479 | self.object_full_path = None 480 | 481 | class CameraLatticeControlsWidget(QtWidgets.QWidget): 482 | def __init__(self, parent=None): 483 | super(CameraLatticeControlsWidget, self).__init__(parent) 484 | self.setWindowTitle('Camera Lattice Controls') 485 | self.setObjectName('Camera Lattice Controls') 486 | 487 | self._lattice = None 488 | self._script_jobs = [] 489 | self._interpolation_changed_from_GUI = False 490 | self._max_bezier_recursion_changed_from_GUI = False 491 | 492 | self._main_layout = _build_layout(False) 493 | self.setLayout(self._main_layout) 494 | 495 | self._create_widgets() 496 | self._connect_signals() 497 | 498 | def _build_affected_object_widget(self): 499 | container = QtWidgets.QWidget() 500 | h_layout = _build_layout(True) 501 | container.setLayout(h_layout) 502 | 503 | self._objects_tree = QtWidgets.QTreeWidget() 504 | self._objects_tree.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 505 | self._objects_tree.header().close() 506 | h_layout.addWidget(self._objects_tree) 507 | 508 | v_layout = _build_layout(False) 509 | h_layout.addLayout(v_layout) 510 | 511 | self._add_object_button = QtWidgets.QPushButton() 512 | self._add_object_button.setFixedSize(40, 40) 513 | self._add_object_button.setIcon(QtGui.QIcon(_get_icons_path() + 'addObject.png')) 514 | self._add_object_button.setIconSize(QtCore.QSize(40, 40)) 515 | self._add_object_button.setToolTip('Add selected object to camera lattice') 516 | 517 | self._remove_object_button = QtWidgets.QPushButton() 518 | self._remove_object_button.setFixedSize(40, 40) 519 | self._remove_object_button.setIcon(QtGui.QIcon(_get_icons_path() + 'deleteObject.png')) 520 | self._remove_object_button.setIconSize(QtCore.QSize(40, 40)) 521 | self._remove_object_button.setToolTip('Remove object from camera lattice') 522 | 523 | v_layout.addWidget(self._add_object_button) 524 | v_layout.addWidget(self._remove_object_button) 525 | v_layout.addWidget(QtWidgets.QWidget(), stretch=1) 526 | 527 | return container 528 | 529 | 530 | def _build_influece_areas_widget(self): 531 | container = QtWidgets.QWidget() 532 | h_layout = _build_layout(True) 533 | container.setLayout(h_layout) 534 | 535 | self._influences_tree = QtWidgets.QTreeWidget() 536 | self._influences_tree.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 537 | self._influences_tree.header().close() 538 | h_layout.addWidget(self._influences_tree) 539 | 540 | v_layout = _build_layout(False) 541 | h_layout.addLayout(v_layout) 542 | 543 | self._create_influencer_button = QtWidgets.QPushButton() 544 | self._create_influencer_button.setFixedSize(40, 40) 545 | self._create_influencer_button.setIcon(QtGui.QIcon(_get_icons_path() + 'createInfluencer.png')) 546 | self._create_influencer_button.setIconSize(QtCore.QSize(40, 40)) 547 | self._create_influencer_button.setToolTip('Create influence area ond add it to camera lattice') 548 | 549 | self._add_influencer_button = QtWidgets.QPushButton() 550 | self._add_influencer_button.setFixedSize(40, 40) 551 | self._add_influencer_button.setIcon(QtGui.QIcon(_get_icons_path() + 'addInfluencer.png')) 552 | self._add_influencer_button.setIconSize(QtCore.QSize(40, 40)) 553 | self._add_influencer_button.setToolTip('Add selected influence area to camera lattice') 554 | 555 | self._remove_influencer_button = QtWidgets.QPushButton() 556 | self._remove_influencer_button.setFixedSize(40, 40) 557 | self._remove_influencer_button.setIcon(QtGui.QIcon(_get_icons_path() + 'deleteInfluencer.png')) 558 | self._remove_influencer_button.setIconSize(QtCore.QSize(40, 40)) 559 | self._remove_influencer_button.setToolTip('Remove influence area from camera lattice') 560 | 561 | v_layout.addWidget(self._create_influencer_button) 562 | v_layout.addWidget(self._add_influencer_button) 563 | v_layout.addWidget(self._remove_influencer_button) 564 | v_layout.addWidget(QtWidgets.QWidget(), stretch=1) 565 | 566 | return container 567 | 568 | 569 | def _create_widgets(self): 570 | _active_parent = QtWidgets.QWidget() 571 | h_layout = _build_layout(True) 572 | _active_parent.setLayout(h_layout) 573 | 574 | self._active_group = QtWidgets.QButtonGroup(_active_parent) 575 | self._on_button = QtWidgets.QRadioButton("On") 576 | self._on_button.setChecked(True) 577 | self._active_group.addButton(self._on_button) 578 | self._off_button = QtWidgets.QRadioButton("Off") 579 | self._active_group.addButton(self._off_button) 580 | h_layout.addWidget(self._on_button) 581 | h_layout.addWidget(self._off_button) 582 | h_layout.addWidget(QtWidgets.QWidget(), 1) 583 | self._main_layout.addWidget(LineWidget("Active:", _active_parent)) 584 | 585 | self._interpolation = QtWidgets.QComboBox() 586 | self._interpolation.addItem("Linear") 587 | self._interpolation.addItem("Bezier") 588 | self._main_layout.addWidget(LineWidget("Interpolation:", self._interpolation)) 589 | 590 | self._max_bezier_recursion = QtWidgets.QSpinBox() 591 | self._max_bezier_recursion_lined_widget = LineWidget("Max Recursion:", self._max_bezier_recursion) 592 | self._main_layout.addWidget(self._max_bezier_recursion_lined_widget) 593 | 594 | tab_widget = QtWidgets.QTabWidget() 595 | tab_widget.addTab(self._build_affected_object_widget(), "Affected Objects") 596 | tab_widget.addTab(self._build_influece_areas_widget(), "Influence Areas") 597 | 598 | self._main_layout.addWidget(tab_widget) 599 | 600 | self._main_layout.addWidget(_create_separator(False)) 601 | 602 | h_layout = _build_layout(True) 603 | self._main_layout.addLayout(h_layout) 604 | 605 | self._select_all_points_button = QtWidgets.QPushButton("Select All Points") 606 | self._select_all_edited_points_button = QtWidgets.QPushButton("Select All Edited Points") 607 | self._select_all_edited_points_button.setToolTip('Select all points not at rest position') 608 | self._select_all_static_edited_points_button = QtWidgets.QPushButton("Select All Static Edited Points") 609 | self._select_all_static_edited_points_button.setToolTip('Select all points not at rest position and no animation') 610 | self._select_all_animated_points_button = QtWidgets.QPushButton("Select All Animated Points") 611 | self._select_all_animated_points_button.setToolTip('Select all animated points') 612 | self._invert_selection_button = QtWidgets.QPushButton("Invert Points Selection") 613 | self._reset_selected_points_to_initial_position = QtWidgets.QPushButton("Reset Selected Points") 614 | self._reset_lattice_button = QtWidgets.QPushButton("Reset All Points") 615 | 616 | v_layout = _build_layout(False) 617 | h_layout.addLayout(v_layout) 618 | v_layout.addWidget(self._select_all_points_button, stretch=1) 619 | v_layout.addWidget(self._select_all_edited_points_button, stretch=1) 620 | v_layout.addWidget(self._select_all_animated_points_button, stretch=1) 621 | v_layout.addWidget(self._select_all_static_edited_points_button, stretch=1) 622 | v_layout.addWidget(self._invert_selection_button, stretch=1) 623 | v_layout.addWidget(_create_separator(False), stretch=1) 624 | v_layout.addWidget(self._reset_selected_points_to_initial_position, stretch=1) 625 | v_layout.addWidget(self._reset_lattice_button, stretch=1) 626 | 627 | v_layout = _build_layout(False) 628 | self._key_selected_on_x_button = QtWidgets.QPushButton("Key On X") 629 | self._key_selected_on_x_button.setFixedWidth(80) 630 | self._key_selected_on_x_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) 631 | self._key_selected_on_y_button = QtWidgets.QPushButton("Key On Y") 632 | self._key_selected_on_y_button.setFixedWidth(80) 633 | self._key_selected_on_y_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) 634 | self._key_selected_button = QtWidgets.QPushButton("Key On XY") 635 | self._key_selected_button.setFixedWidth(80) 636 | self._key_selected_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) 637 | 638 | v_layout.addWidget(self._key_selected_on_x_button) 639 | v_layout.addWidget(self._key_selected_on_y_button) 640 | v_layout.addWidget(self._key_selected_button) 641 | 642 | h_layout.addWidget(_create_separator(True)) 643 | h_layout.addLayout(v_layout) 644 | 645 | def _connect_signals(self): 646 | self._active_group.buttonClicked.connect(self._active_group_clicked) 647 | 648 | self._objects_tree.itemClicked.connect(self._objects_selection_changed) 649 | self._influences_tree.itemClicked.connect(self._influences_selection_changed) 650 | self._interpolation.currentIndexChanged.connect(self._interpolation_changed) 651 | 652 | self._max_bezier_recursion.valueChanged.connect(self._max_bezier_recursion_changed) 653 | self._max_bezier_recursion_lined_widget.setVisible(False) 654 | 655 | self._add_object_button.clicked.connect(self._add_object_button_clicked) 656 | self._remove_object_button.clicked.connect(self._remove_object_button_clicked) 657 | 658 | self._create_influencer_button.clicked.connect(self._create_influencer_button_clicked) 659 | self._add_influencer_button.clicked.connect(self._add_influencer_button_clicked) 660 | self._remove_influencer_button.clicked.connect(self._remove_influencer_button_clicked) 661 | 662 | self._select_all_points_button.clicked.connect(self._select_all_points_button_clicked) 663 | self._select_all_edited_points_button.clicked.connect(self._select_all_edited_points_button_clicked) 664 | self._select_all_animated_points_button.clicked.connect(self._select_all_animated_points_button_clicked) 665 | self._select_all_static_edited_points_button.clicked.connect(self._select_all_static_edited_points_button_clicked) 666 | self._invert_selection_button.clicked.connect(self._invert_selection_button_clicked) 667 | self._reset_selected_points_to_initial_position.clicked.connect(self._reset_selected_points_to_initial_position_clicked) 668 | self._reset_lattice_button.clicked.connect(self._reset_lattice_button_clicked) 669 | self._key_selected_button.clicked.connect(self._key_selected_button_clicked) 670 | self._key_selected_on_x_button.clicked.connect(self._key_selected_on_x_button_clicked) 671 | self._key_selected_on_y_button.clicked.connect(self._key_selected_on_y_button_clicked) 672 | 673 | def _pre_lattice_point_selection(self): 674 | cmds.select(self._lattice, r=True) 675 | cmds.hilite(self._lattice) 676 | cmds.selectType(ocm=True, alc=False) 677 | cmds.selectType(ocm=True, vertex=True) 678 | 679 | @staticmethod 680 | def _is_value_changed(val1, val2): 681 | return (val1 < val2 - 0.0001) or (val1 > val2 + 0.0001) 682 | 683 | def _get_lattice_divisions(self): 684 | sD = cmds.getAttr(self._lattice + ".sDivisions") 685 | tD = cmds.getAttr(self._lattice + ".tDivisions") 686 | return sD, tD 687 | 688 | def _is_item_in_affected_objs(self, obj): 689 | for i in range(self._objects_tree.topLevelItemCount()): 690 | item = self._objects_tree.topLevelItem(i) 691 | if item.object_full_path == obj: 692 | return True 693 | return False 694 | 695 | def _active_group_clicked(self): 696 | if self._on_button.isChecked(): 697 | cmds.setAttr(self._lattice + "." + LATTICE_ACTIVE_ATTR, 1) 698 | else: 699 | cmds.setAttr(self._lattice + "." + LATTICE_ACTIVE_ATTR, 0) 700 | 701 | def _create_influencer_button_clicked(self): 702 | cmds.undoInfo(openChunk=True, chunkName='tcCreateInfluenceAreaToCameraLattice') 703 | try: 704 | influencer = _create_influence_area(self._lattice) 705 | item = self._create_influencer_tree_item(influencer) 706 | self._influences_tree.addTopLevelItem(item) 707 | except: 708 | traceback.print_exc(file=sys.stdout) 709 | cmds.undoInfo(closeChunk=True) 710 | 711 | def _add_influencer_button_clicked(self): 712 | cmds.undoInfo(openChunk=True, chunkName='tcAddInfluenceAreaToCameraLattice') 713 | try: 714 | for influencer in _get_selected_influencers(): 715 | if _apply_influence_area_to_lattice(self._lattice, influencer): 716 | item = self._create_influencer_tree_item(influencer) 717 | self._influences_tree.addTopLevelItem(item) 718 | except: 719 | traceback.print_exc(file=sys.stdout) 720 | cmds.undoInfo(closeChunk=True) 721 | 722 | def _remove_influencer_button_clicked(self): 723 | items = self._influences_tree.selectedItems() 724 | nodes = [] 725 | for item in items: 726 | index = self._influences_tree.indexFromItem(item) 727 | node = _get_infuencer_full_path(self._lattice, item.influencer) 728 | if node: 729 | nodes.append(node) 730 | self._influences_tree.takeTopLevelItem(index.row()) 731 | 732 | if nodes: 733 | cmds.undoInfo(openChunk=True, chunkName='tcRemoveInfluenceAreaFromCameraLattice') 734 | _disconnect_influencers(self._lattice, nodes) 735 | cmds.undoInfo(closeChunk=True) 736 | 737 | self._remove_influencer_button.setEnabled(bool(self._influences_tree.selectedItems())) 738 | 739 | def _add_object_button_clicked(self): 740 | selection = cmds.ls(sl=True, l=True, type="transform") 741 | if not selection: 742 | cmds.warning("Camera Lattice: Please select mesh transforms and add them to the lattice.") 743 | return 744 | 745 | cmds.undoInfo(openChunk=True, chunkName='tcAddObjectToCameraLattice') 746 | try: 747 | for s in selection: 748 | #if the lattice is selected then skip it 749 | if _is_lattice(s): 750 | cmds.warning('Camera Lattice: Cannot add camera lattices to the list of the affected objects.') 751 | continue 752 | 753 | if not _is_deformable(s): 754 | cmds.warning('Camera Lattice: %s is not a deformable object.' % s) 755 | continue 756 | 757 | if self._is_item_in_affected_objs(s): 758 | cmds.warning('Camera Lattice: This object is already affected by this lattice.') 759 | continue 760 | 761 | deformer = _apply_camera_lattice(s, self._lattice) 762 | item = self._create_object_tree_item(deformer, s) 763 | self._objects_tree.addTopLevelItem(item) 764 | except: 765 | traceback.print_exc(file=sys.stdout) 766 | cmds.undoInfo(closeChunk=True) 767 | 768 | def _remove_object_button_clicked(self): 769 | items = self._objects_tree.selectedItems() 770 | nodes = [] 771 | for item in items: 772 | index = self._objects_tree.indexFromItem(item) 773 | nodes.append(item.camera_lattice_deformer_node) 774 | self._objects_tree.takeTopLevelItem(index.row()) 775 | 776 | if nodes: 777 | cmds.undoInfo(openChunk=True, chunkName='tcRemoveObjectFromCameraLattice') 778 | try: 779 | cmds.delete(nodes) 780 | except: 781 | traceback.print_exc(file=sys.stdout) 782 | 783 | cmds.undoInfo(closeChunk=True) 784 | 785 | self._remove_object_button.setEnabled(bool(self._objects_tree.selectedItems())) 786 | 787 | def _select_all_points_button_clicked(self): 788 | sD, tD = self._get_lattice_divisions() 789 | 790 | cmds.undoInfo(openChunk=True, chunkName='tcSelectAllCameraLatticePoints') 791 | try: 792 | self._pre_lattice_point_selection() 793 | cmds.select(self._lattice + ".vtx[0:%d]" % (sD * tD - 1), r=True) 794 | except: 795 | traceback.print_exc(file=sys.stdout) 796 | cmds.undoInfo(closeChunk=True) 797 | 798 | def _select_all_static_edited_points_button_clicked(self): 799 | sD, tD = self._get_lattice_divisions() 800 | 801 | x_step = 1.0 / (sD - 1) 802 | y_step = 1.0 / (tD - 1) 803 | offset = 0.5 804 | 805 | cmds.undoInfo(openChunk=True, chunkName='tcSelectAllCameraLatticeEditedPoints') 806 | try: 807 | self._pre_lattice_point_selection() 808 | cmds.select(cl=True) 809 | 810 | for t in range(tD): 811 | for s in range(sD): 812 | attr_name = self._build_point_string(s, t, sD) 813 | vector = cmds.xform(attr_name, q=True, translation=True) 814 | edited = self._is_value_changed(vector[0], s * x_step - offset) or self._is_value_changed(vector[1], t * y_step - offset) 815 | index = s + t * sD 816 | animated = cmds.listConnections(self._lattice + ".pnts[%d].pntx" % index) or cmds.listConnections(self._lattice + ".pnts[%d].pnty" % index) 817 | if edited and not animated: 818 | cmds.select(attr_name, add=True) 819 | except: 820 | traceback.print_exc(file=sys.stdout) 821 | cmds.undoInfo(closeChunk=True) 822 | 823 | def _select_all_edited_points_button_clicked(self): 824 | sD, tD = self._get_lattice_divisions() 825 | 826 | x_step = 1.0 / (sD - 1) 827 | y_step = 1.0 / (tD - 1) 828 | offset = 0.5 829 | 830 | cmds.undoInfo(openChunk=True, chunkName='tcSelectAllCameraLatticeEditedPoints') 831 | try: 832 | self._pre_lattice_point_selection() 833 | cmds.select(cl=True) 834 | 835 | for t in range(tD): 836 | for s in range(sD): 837 | attr_name = self._build_point_string(s, t, sD) 838 | vector = cmds.xform(attr_name, q=True, translation=True) 839 | if (self._is_value_changed(vector[0], s * x_step - offset) or 840 | self._is_value_changed(vector[1], t * y_step - offset)): 841 | cmds.select(attr_name, add=True) 842 | except: 843 | traceback.print_exc(file=sys.stdout) 844 | cmds.undoInfo(closeChunk=True) 845 | 846 | def _select_all_animated_points_button_clicked(self): 847 | sD, tD = self._get_lattice_divisions() 848 | 849 | x_step = 1.0 / (sD - 1) 850 | y_step = 1.0 / (tD - 1) 851 | offset = 0.5 852 | 853 | cmds.undoInfo(openChunk=True, chunkName='tcSelectAllCameraLatticeAnimatedPoints') 854 | try: 855 | self._pre_lattice_point_selection() 856 | cmds.select(cl=True) 857 | for t in range(tD): 858 | for s in range(sD): 859 | index = s + t * sD 860 | if (cmds.listConnections(self._lattice + ".pnts[%d].pntx" % index) or 861 | cmds.listConnections(self._lattice + ".pnts[%d].pnty" % index)): 862 | cmds.select(self._lattice + ".pnts[%d]" % index, add=True) 863 | except: 864 | traceback.print_exc(file=sys.stdout) 865 | cmds.undoInfo(closeChunk=True) 866 | 867 | def _invert_selection_button_clicked(self): 868 | selection = cmds.ls(sl=True, fl=True, l=True) 869 | if not selection: 870 | self._select_all_points_button_clicked() 871 | return 872 | 873 | if not self._lattice + ".vtx" in selection[0]: 874 | cmds.error('Camera Lattice: no lattice points selected. Cannot inver selection.') 875 | return 876 | 877 | sD, tD = self._get_lattice_divisions() 878 | sel_set = set([str(s) for s in selection]) 879 | cmds.undoInfo(openChunk=True, chunkName='tcInvertCameraLatticePointSelection') 880 | try: 881 | self._pre_lattice_point_selection() 882 | cmds.select(cl=True) 883 | for s in range(sD): 884 | for t in range(tD): 885 | pt = self._build_point_string(s, t, sD) 886 | if not pt in sel_set: 887 | cmds.select(pt, add=True) 888 | except: 889 | traceback.print_exc(file=sys.stdout) 890 | cmds.undoInfo(closeChunk=True) 891 | 892 | @staticmethod 893 | def get_point_index_from_string(item): 894 | match = re.findall('(?<=\[)\d+(?=\])', item) 895 | return int(match[0]) 896 | 897 | def _reset_selected_points_to_initial_position_clicked(self): 898 | selection = cmds.ls(sl=True, fl=True, l=True) 899 | if not selection: 900 | return 901 | 902 | if not self._lattice + ".vtx" in selection[0]: 903 | cmds.error('Camera Lattice: no lattice points selected.') 904 | return 905 | 906 | sD, tD = self._get_lattice_divisions() 907 | 908 | x_step = 1.0 / (sD - 1) 909 | y_step = 1.0 / (tD - 1) 910 | offset = 0.5 911 | 912 | cmds.undoInfo(openChunk=True, chunkName='tcResetSelectedCameraLatticePoints') 913 | try: 914 | for pt in selection: 915 | cmds.setAttr(pt + '.pntx', 0.0) 916 | cmds.setAttr(pt + '.pnty', 0.0) 917 | except: 918 | traceback.print_exc(file=sys.stdout) 919 | cmds.undoInfo(closeChunk=True) 920 | 921 | def _reset_lattice_button_clicked(self): 922 | sD, tD = self._get_lattice_divisions() 923 | 924 | x_step = 1.0 / (sD - 1) 925 | y_step = 1.0 / (tD - 1) 926 | offset = 0.5 927 | 928 | cmds.undoInfo(openChunk=True, chunkName='tcResetCameraLatticePoints') 929 | try: 930 | for s in range(sD): 931 | for t in range(tD): 932 | vrt = self._build_point_string(s, t, sD) 933 | cmds.setAttr(vrt + '.pntx', 0.0) 934 | cmds.setAttr(vrt + '.pnty', 0.0) 935 | except: 936 | traceback.print_exc(file=sys.stdout) 937 | cmds.undoInfo(closeChunk=True) 938 | 939 | def _key_selected_on_x_button_clicked(self): 940 | selection = cmds.ls(sl=True, fl=True, l=True) 941 | if not selection: 942 | return 943 | 944 | if not self._lattice + ".vtx" in selection[0]: 945 | cmds.error('Camera Lattice: no lattice points selected. Cannot key points.') 946 | return 947 | 948 | cmds.undoInfo(openChunk=True, chunkName='tcKeyCameraLatticePointsOnX') 949 | try: 950 | for s in selection: 951 | cmds.setKeyframe(s.replace('.vtx[', '.pnts[') + '.pntx', cp=True) 952 | except: 953 | traceback.print_exc(file=sys.stdout) 954 | cmds.undoInfo(closeChunk=True) 955 | 956 | def _key_selected_on_y_button_clicked(self): 957 | selection = cmds.ls(sl=True, fl=True, l=True) 958 | if not selection: 959 | return 960 | 961 | if not self._lattice + ".vtx" in selection[0]: 962 | cmds.error('Camera Lattice: no lattice points selected. Cannot key points.') 963 | return 964 | 965 | cmds.undoInfo(openChunk=True, chunkName='tcKeyCameraLatticePointsOnY') 966 | try: 967 | for s in selection: 968 | cmds.setKeyframe(s.replace('.vtx[', '.pnts[') + '.pnty', cp=True) 969 | except: 970 | traceback.print_exc(file=sys.stdout) 971 | cmds.undoInfo(closeChunk=True) 972 | 973 | def _key_selected_button_clicked(self): 974 | selection = cmds.ls(sl=True, fl=True, l=True) 975 | if not selection: 976 | return 977 | 978 | if not self._lattice + ".vtx" in selection[0]: 979 | cmds.error('Camera Lattice: no lattice points selected. Cannot key points.') 980 | return 981 | 982 | cmds.undoInfo(openChunk=True, chunkName='tcKeyCameraLatticePointsOnXY') 983 | try: 984 | for s in selection: 985 | cmds.setKeyframe(s.replace('.vtx[', '.pnts[') + '.pntx', cp=True) 986 | cmds.setKeyframe(s.replace('.vtx[', '.pnts[') + '.pnty', cp=True) 987 | except: 988 | traceback.print_exc(file=sys.stdout) 989 | cmds.undoInfo(closeChunk=True) 990 | 991 | def clear_object_tree(self): 992 | self._objects_tree.clear() 993 | 994 | def clear_influence_tree(self): 995 | self._influences_tree.clear() 996 | 997 | def _objects_selection_changed(self): 998 | items = self._objects_tree.selectedItems() 999 | self._remove_object_button.setEnabled(bool(items)) 1000 | 1001 | objs = [i.object_full_path for i in items] 1002 | cmds.select(objs, r=True) 1003 | 1004 | def _influences_selection_changed(self): 1005 | items = self._influences_tree.selectedItems() 1006 | self._remove_influencer_button.setEnabled(bool(items)) 1007 | 1008 | objs = [i.influencer for i in items] 1009 | cmds.select(objs, r=True) 1010 | 1011 | def _interpolation_changed(self): 1012 | self._interpolation_changed_from_GUI = True 1013 | cmds.setAttr(self._lattice + '.' + INTERPOLATION_ATTR, self._interpolation.currentIndex()) 1014 | self._max_bezier_recursion_lined_widget.setVisible(self._interpolation.currentIndex() == 1) 1015 | 1016 | def _max_bezier_recursion_changed(self): 1017 | self._max_bezier_recursion_changed_from_GUI = True 1018 | cmds.setAttr(self._lattice + '.' + MAX_BEZIER_RECURSION_ATTR, self._max_bezier_recursion.value()) 1019 | 1020 | def _create_object_tree_item(self, deformer, object): 1021 | item = CameraLatticeTreeWidgetItem() 1022 | item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) 1023 | item.setText(0, object.split('|')[-1]) 1024 | item.setToolTip(0, object) 1025 | item.camera_lattice_deformer_node = deformer 1026 | item.object_full_path = object 1027 | return item 1028 | 1029 | def _create_influencer_tree_item(self, influencer): 1030 | item = CameraLatticeTreeWidgetItem() 1031 | item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) 1032 | item.setText(0, influencer.split('|')[-1]) 1033 | item.setToolTip(0, influencer) 1034 | item.influencer = influencer 1035 | return item 1036 | 1037 | def set_lattice(self, lattice): 1038 | self._lattice = lattice 1039 | 1040 | self.kill_script_jobs() 1041 | self._start_script_jobs() 1042 | 1043 | active = cmds.getAttr(lattice + '.' + LATTICE_ACTIVE_ATTR) 1044 | self._on_button.setChecked(active) 1045 | self._off_button.setChecked(not active) 1046 | 1047 | #set interpolation value 1048 | interpolation = cmds.getAttr(lattice + '.' + INTERPOLATION_ATTR) 1049 | self._interpolation.setCurrentIndex(interpolation) 1050 | 1051 | #set maximum bezier recursion 1052 | max_recursion = cmds.getAttr(lattice + '.' + MAX_BEZIER_RECURSION_ATTR) 1053 | min = cmds.attributeQuery(MAX_BEZIER_RECURSION_ATTR, node=lattice, minimum=True) 1054 | max = cmds.attributeQuery(MAX_BEZIER_RECURSION_ATTR, node=lattice, maximum=True) 1055 | self._max_bezier_recursion.setValue(max_recursion) 1056 | self._max_bezier_recursion.setMaximum(int(max[0])) 1057 | self._max_bezier_recursion.setMinimum(int(min[0])) 1058 | 1059 | self._max_bezier_recursion_lined_widget.setVisible(interpolation == 1) 1060 | 1061 | self._refresh_object_tree() 1062 | self._refresh_influence_tree() 1063 | 1064 | def _refresh_object_tree(self): 1065 | self.clear_object_tree() 1066 | #populate tree 1067 | objects = _get_all_affected_objects(self._lattice) 1068 | for key, value in objects.iteritems(): 1069 | item = self._create_object_tree_item(key, value) 1070 | self._objects_tree.addTopLevelItem(item) 1071 | 1072 | self._remove_object_button.setEnabled(False) 1073 | 1074 | def _refresh_influence_tree(self): 1075 | self.clear_influence_tree() 1076 | 1077 | influencers = _get_all_influencers(self._lattice) 1078 | for i in influencers: 1079 | item = self._create_influencer_tree_item(i) 1080 | self._influences_tree.addTopLevelItem(item) 1081 | 1082 | self._remove_influencer_button.setEnabled(False) 1083 | 1084 | def _interpolation_changed_from_maya(self): 1085 | if not self._interpolation_changed_from_GUI: 1086 | interpolation = cmds.getAttr(self._lattice + '.' + INTERPOLATION_ATTR) 1087 | self._interpolation.setCurrentIndex(interpolation) 1088 | self._max_bezier_recursion_lined_widget.setVisible(interpolation == 1) 1089 | self._interpolation_changed_from_GUI = False 1090 | 1091 | def _max_bezier_recursion_changed_from_maya(self): 1092 | if not self._max_bezier_recursion_changed_from_GUI: 1093 | max_bezier_recursion = cmds.getAttr(self._lattice + '.' + MAX_BEZIER_RECURSION_ATTR) 1094 | self._max_bezier_recursion.setValue(max_bezier_recursion) 1095 | self._max_bezier_recursion_changed_from_GUI = False 1096 | 1097 | def _build_point_string(self, s, t, sD): 1098 | return self._lattice + '.vtx[%d]' % (s + t * sD) 1099 | 1100 | def _start_script_jobs(self): 1101 | id = cmds.scriptJob(attributeChange=[self._lattice + '.' + INTERPOLATION_ATTR, self._interpolation_changed_from_maya]) 1102 | self._script_jobs.append(id) 1103 | id = cmds.scriptJob(attributeChange=[self._lattice + '.' + MAX_BEZIER_RECURSION_ATTR, self._max_bezier_recursion_changed_from_maya]) 1104 | self._script_jobs.append(id) 1105 | 1106 | def undo_triggered(self): 1107 | if not self._lattice: 1108 | return 1109 | name = cmds.undoInfo(q=True, redoName=True) 1110 | if str(name) in ['tcDeleteCameraLattice', 'tcAddObjectToCameraLattice', 'tcRemoveObjectFromCameraLattice']: 1111 | self._refresh_object_tree() 1112 | if str(name) in ['tcDeleteCameraLattice', 'tcAddInfluenceAreaToCameraLattice', 'tcRemoveInfluenceAreaFromCameraLattice', 1113 | 'tcCreateInfluenceAreaToCameraLattice']: 1114 | self._refresh_influence_tree() 1115 | 1116 | def redo_triggered(self): 1117 | if not self._lattice: 1118 | return 1119 | name = cmds.undoInfo(q=True, undoName=True) 1120 | if str(name) in ['tcDeleteCameraLattice', 'tcAddObjectToCameraLattice', 'tcRemoveObjectFromCameraLattice']: 1121 | self._refresh_object_tree() 1122 | if str(name) in ['tcDeleteCameraLattice', 'tcAddInfluenceAreaToCameraLattice', 'tcRemoveInfluenceAreaFromCameraLattice', 1123 | 'tcCreateInfluenceAreaToCameraLattice']: 1124 | self._refresh_influence_tree() 1125 | 1126 | def kill_script_jobs(self): 1127 | for id in self._script_jobs: 1128 | if cmds.scriptJob(exists=id): 1129 | cmds.scriptJob(kill=id) 1130 | self._script_jobs = [] 1131 | 1132 | 1133 | class CameraLatticeWidget(QtWidgets.QDialog): 1134 | def __init__(self, parent=None): 1135 | super(CameraLatticeWidget, self).__init__(parent) 1136 | self.setWindowTitle('Camera Lattice') 1137 | self.setObjectName('Camera Lattice') 1138 | 1139 | self._main_layout = _build_layout(False) 1140 | self.setLayout(self._main_layout) 1141 | 1142 | self._lattices = [] 1143 | 1144 | self._script_jobs = [] 1145 | self._selected_camera = None 1146 | 1147 | self._combo_refreshing = False 1148 | 1149 | self._create_widgets() 1150 | self._connect_signals() 1151 | 1152 | def _create_widgets(self): 1153 | about = QtWidgets.QLabel() 1154 | about.setPixmap(QtGui.QPixmap(_get_icons_path() + 'abouts.png')) 1155 | self._main_layout.addWidget(about) 1156 | 1157 | self._main_layout.addWidget(_create_separator(False)) 1158 | 1159 | h_layout = _build_layout(True) 1160 | self._main_layout.addLayout(h_layout) 1161 | 1162 | v_layout = _build_layout(False) 1163 | h_layout.addLayout(v_layout) 1164 | 1165 | self._camera_name = QtWidgets.QLabel() 1166 | self._camera_name.setTextFormat(QtCore.Qt.RichText) 1167 | v_layout.addWidget(LineWidget("Lattice Camera:", self._camera_name)) 1168 | 1169 | h_layout2 = _build_layout(True) 1170 | 1171 | self._lattices_combo = QtWidgets.QComboBox() 1172 | h_layout2.addWidget(LineWidget("Current Lattice:", self._lattices_combo)) 1173 | self._lattices_combo.setEnabled(False) 1174 | self._refresh_button = QtWidgets.QPushButton() 1175 | self._refresh_button.setIcon(QtGui.QIcon(_get_icons_path() + 'refreshIcon.png')) 1176 | self._refresh_button.setIconSize(QtCore.QSize(18, 18)) 1177 | self._refresh_button.setFixedSize(20, 20) 1178 | h_layout2.addWidget(self._refresh_button) 1179 | self._refresh_button.setEnabled(False) 1180 | 1181 | v_layout.addLayout(h_layout2) 1182 | self._set_camera_name('None') 1183 | 1184 | h_layout.addWidget(_create_separator(True)) 1185 | 1186 | v_layout = _build_layout(False) 1187 | h_layout.addLayout(v_layout) 1188 | 1189 | self._create_lattice_button = QtWidgets.QPushButton() 1190 | self._create_lattice_button.setFixedSize(40, 40) 1191 | self._create_lattice_button.setIcon(QtGui.QIcon(_get_icons_path() + 'addLattice.png')) 1192 | self._create_lattice_button.setIconSize(QtCore.QSize(38, 38)) 1193 | self._create_lattice_button.setEnabled(False) 1194 | self._delete_lattice_button = QtWidgets.QPushButton() 1195 | self._delete_lattice_button.setIcon(QtGui.QIcon(_get_icons_path() + 'deleteLattice.png')) 1196 | self._delete_lattice_button.setIconSize(QtCore.QSize(38, 38)) 1197 | self._delete_lattice_button.setFixedSize(40, 40) 1198 | self._delete_lattice_button.setEnabled(False) 1199 | 1200 | v_layout.addWidget(self._create_lattice_button) 1201 | v_layout.addWidget(self._delete_lattice_button) 1202 | 1203 | self._main_layout.addWidget(_create_separator()) 1204 | 1205 | self._controls = CameraLatticeControlsWidget() 1206 | self._controls.setEnabled(False) 1207 | self._main_layout.addWidget(self._controls) 1208 | 1209 | self._main_layout.addWidget(QtWidgets.QWidget(), stretch=1) 1210 | 1211 | def _connect_signals(self): 1212 | self._create_lattice_button.clicked.connect(self._create_lattice_button_clicked) 1213 | self._delete_lattice_button.clicked.connect(self._delete_lattice_button_clicked) 1214 | 1215 | self._refresh_button.clicked.connect(self._refresh_widgets) 1216 | 1217 | self._lattices_combo.currentIndexChanged.connect(self._lattice_combo_changed) 1218 | 1219 | def _lattice_combo_changed(self, index): 1220 | if not self._combo_refreshing and index != -1: 1221 | cmds.undoInfo(openChunk=True, chunkName='tcCameraLatticeSelection') 1222 | try: 1223 | self._hide_all_lattices() 1224 | cmds.setAttr(self._lattices[index]+".visibility", True) 1225 | cmds.select(self._lattices[index], r=True) 1226 | except: 1227 | traceback.print_exc(file=sys.stdout) 1228 | cmds.undoInfo(closeChunk=True) 1229 | self._refresh_widgets(self._lattices[index]) 1230 | 1231 | def _set_camera_name(self, name): 1232 | self._camera_name.setText("

" + name + "


") 1233 | self._camera_name.setToolTip(name) 1234 | 1235 | def _create_lattice_button_clicked(self): 1236 | x_div, y_div, ok = CameraLatticeCreateDialog.get_result() 1237 | if ok: 1238 | lattice = None 1239 | cmds.undoInfo(openChunk=True, chunkName='tcCreateCameraLattice') 1240 | try: 1241 | lattice = _create_camera_lattice(self._selected_camera, x_div, y_div) 1242 | self._hide_all_lattices() 1243 | except: 1244 | traceback.print_exc(file=sys.stdout) 1245 | cmds.undoInfo(closeChunk=True) 1246 | self._refresh_widgets(selected_lattice = lattice) 1247 | 1248 | def _delete_lattice_button_clicked(self): 1249 | if self._lattices: 1250 | cmds.undoInfo(openChunk=True, chunkName='tcDeleteCameraLattice') 1251 | try: 1252 | index = self._lattices_combo.currentIndex() 1253 | _delete_lattice_deformers(self._lattices[index]) 1254 | cmds.delete(self._lattices[index]) 1255 | 1256 | if len(self._lattices) > 1: 1257 | cmds.setAttr(self._lattices[1 if index == 0 else 0]+".visibility", True) 1258 | except: 1259 | traceback.print_exc(file=sys.stdout) 1260 | cmds.undoInfo(closeChunk=True) 1261 | self._refresh_widgets() 1262 | 1263 | def _refresh_lattices_combo(self, lattices): 1264 | self._lattices = lattices 1265 | 1266 | self._lattices_combo.clear() 1267 | 1268 | self._lattices.sort() 1269 | for l in self._lattices: 1270 | self._lattices_combo.addItem(l.split("|")[-1]) 1271 | 1272 | def _get_lattice_index(self, lattice): 1273 | for i in range(len(self._lattices)): 1274 | if lattice == self._lattices[i]: 1275 | return i 1276 | return -1 1277 | 1278 | def _hide_all_lattices(self): 1279 | for l in self._lattices: 1280 | cmds.setAttr(l+".visibility", False) 1281 | 1282 | def _get_selected_lattice(self): 1283 | for l in self._lattices: 1284 | if cmds.getAttr(l+".visibility"): 1285 | return l 1286 | 1287 | def _refresh_widgets(self, selected_lattice = None): 1288 | lattices = _get_lattices_from_camera(self._selected_camera) 1289 | has_lattices = bool(lattices) 1290 | 1291 | self._controls.clear_object_tree() 1292 | self._controls.clear_influence_tree() 1293 | self._controls.setEnabled(has_lattices) 1294 | 1295 | self._delete_lattice_button.setEnabled(has_lattices) 1296 | 1297 | self._combo_refreshing = True 1298 | 1299 | self._lattices_combo.setEnabled(has_lattices) 1300 | self._refresh_button.setEnabled(has_lattices) 1301 | if has_lattices: 1302 | self._refresh_lattices_combo(lattices) 1303 | if not selected_lattice: 1304 | selected_lattice = self._get_selected_lattice() 1305 | 1306 | self._lattices_combo.setCurrentIndex(self._get_lattice_index(selected_lattice)) 1307 | self._controls.set_lattice(selected_lattice) 1308 | else: 1309 | self._lattices = [] 1310 | self._lattices_combo.clear() 1311 | 1312 | self._controls.kill_script_jobs() 1313 | self._controls._lattice = None 1314 | 1315 | self._combo_refreshing = False 1316 | 1317 | def _selection_changed(self): 1318 | selection = cmds.ls(sl=True, l=True) 1319 | camera = _get_camera(selection) 1320 | 1321 | if not camera or camera == self._selected_camera: 1322 | return 1323 | 1324 | self._create_lattice_button.setEnabled(True) 1325 | 1326 | self._selected_camera = camera 1327 | self._set_camera_name(self._selected_camera.split('|')[-1]) 1328 | self._refresh_widgets() 1329 | 1330 | def _start_script_jobs(self): 1331 | self._script_jobs = [] 1332 | id = cmds.scriptJob(event=["SelectionChanged", self._selection_changed]) 1333 | self._script_jobs.append(id) 1334 | id = cmds.scriptJob(event=["deleteAll", self._delete_all_triggered]) 1335 | self._script_jobs.append(id) 1336 | id = cmds.scriptJob(event=["NameChanged", self._name_changed_triggered]) 1337 | self._script_jobs.append(id) 1338 | id = cmds.scriptJob(event=["Undo", self._undo_triggered]) 1339 | self._script_jobs.append(id) 1340 | id = cmds.scriptJob(event=["Redo", self._redo_triggered]) 1341 | self._script_jobs.append(id) 1342 | 1343 | def _undo_triggered(self): 1344 | self._controls.undo_triggered() 1345 | 1346 | if self._selected_camera: 1347 | name = cmds.undoInfo(q=True, redoName=True) 1348 | if str(name) in ['tcCreateCameraLattice', 'tcDeleteCameraLattice', 'tcCameraLatticeSelection']: 1349 | self._refresh_widgets() 1350 | 1351 | def _delete_all_triggered(self): 1352 | self._selected_camera = None 1353 | self._set_camera_name('None') 1354 | 1355 | self._controls.kill_script_jobs() 1356 | self._controls._lattice = None 1357 | self._controls.clear_object_tree() 1358 | self._controls.clear_influence_tree() 1359 | self._controls.setEnabled(False) 1360 | 1361 | self._create_lattice_button.setEnabled(False) 1362 | self._delete_lattice_button.setEnabled(False) 1363 | 1364 | self._combo_refreshing = True 1365 | self._lattices_combo.clear() 1366 | self._lattices_combo.setEnabled(False) 1367 | self._refresh_button.setEnabled(False) 1368 | self._combo_refreshing = False 1369 | 1370 | def _redo_triggered(self): 1371 | self._controls.redo_triggered() 1372 | 1373 | if self._selected_camera: 1374 | name = cmds.undoInfo(q=True, undoName=True) 1375 | if str(name) in ['tcCreateCameraLattice', 'tcDeleteCameraLattice', 'tcCameraLatticeSelection']: 1376 | self._refresh_widgets() 1377 | 1378 | def _name_changed_triggered(self): 1379 | if not self._selected_camera: 1380 | return 1381 | 1382 | lattices = _get_lattices_from_camera(self._selected_camera) 1383 | if not lattices: 1384 | return 1385 | 1386 | if self._was_lattice_renamed(): 1387 | self._combo_refreshing = True 1388 | self._refresh_lattices_combo(lattices) 1389 | selected_lattice = self._get_selected_lattice() 1390 | self._lattices_combo.setCurrentIndex(self._get_lattice_index(selected_lattice)) 1391 | self._controls.set_lattice(selected_lattice) 1392 | self._combo_refreshing = False 1393 | else: 1394 | self._controls._refresh_object_tree() 1395 | self._controls._refresh_influence_tree() 1396 | 1397 | def _was_lattice_renamed(self): 1398 | for l in self._lattices: 1399 | if not cmds.objExists(l): 1400 | return True 1401 | return False 1402 | 1403 | def show(self): 1404 | if self._selected_camera: 1405 | self._refresh_widgets() 1406 | super(CameraLatticeWidget, self).show() 1407 | 1408 | def closeEvent(self, event): 1409 | for id in self._script_jobs: 1410 | if cmds.scriptJob(exists=id): 1411 | cmds.scriptJob(kill=id) 1412 | self._script_jobs = [] 1413 | 1414 | self._controls.kill_script_jobs() 1415 | 1416 | super(CameraLatticeWidget, self).closeEvent(event) 1417 | 1418 | 1419 | camera_lattice_widget = None 1420 | def get_camera_lattice_widget(): 1421 | global camera_lattice_widget 1422 | if camera_lattice_widget is None: 1423 | camera_lattice_widget = CameraLatticeWidget() 1424 | return camera_lattice_widget 1425 | 1426 | def run(): 1427 | if not cmds.pluginInfo('tcCameraLattice', q=1, l=1): 1428 | cmds.loadPlugin('tcCameraLattice', quiet=True) 1429 | 1430 | w = get_camera_lattice_widget() 1431 | w._start_script_jobs() 1432 | w._selection_changed() 1433 | w.show() 1434 | w.raise_() 1435 | 1436 | -------------------------------------------------------------------------------- /source/PluginMain.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // PluginMain.cpp 3 | // cameraLattice 4 | // 5 | // Created by Daniele Federico on 14/12/14. 6 | // 7 | // 8 | 9 | #include 10 | #include 11 | #include "cameraLattice.h" 12 | #include "cameraLatticeTranslator.h" 13 | #include "cameraLatticeInfluenceLocator.h" 14 | 15 | extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; } 16 | 17 | 18 | MStatus initializePlugin( MObject obj ) 19 | { 20 | MStatus status = MStatus::kSuccess; 21 | MFnPlugin plugin( obj, "ToolChefs_CameraLattice", "1.1", "Any"); 22 | MString errorString; 23 | 24 | std::string licenseName = "tcAnimationTools"; 25 | std::string product = "tcCameraLattice"; 26 | 27 | int count = 1; 28 | const char *ver = "1.0"; 29 | 30 | status = plugin.registerNode( "tcCameraLatticeDeformer", CameraLattice::id, CameraLattice::creator, CameraLattice::initialize, MPxNode::kDeformerNode ); 31 | if(!status) 32 | { 33 | MGlobal::displayError("tcCameraLatticeDeformer failed registration"); 34 | return status; 35 | } 36 | 37 | status = plugin.registerNode( "tcCameraLatticeTranslator", CameraLatticeTranslator::id, CameraLatticeTranslator::creator, 38 | CameraLatticeTranslator::initialize); 39 | if(!status) 40 | { 41 | MGlobal::displayError("tcCameraLatticeTranslator failed registration"); 42 | return status; 43 | } 44 | 45 | status = plugin.registerNode("tcCameraLatticeInfluenceAreaLocator", 46 | CameraLatticeInfluenceLocator::id, 47 | &CameraLatticeInfluenceLocator::creator, 48 | &CameraLatticeInfluenceLocator::initialize, 49 | MPxNode::kLocatorNode, 50 | &CameraLatticeInfluenceLocator::drawDbClassification); 51 | if (!status) { 52 | status.perror("tcCameraLatticeInfluenceAreaLocator failed registration"); 53 | return status; 54 | } 55 | 56 | status = MHWRender::MDrawRegistry::registerDrawOverrideCreator( 57 | CameraLatticeInfluenceLocator::drawDbClassification, 58 | CameraLatticeInfluenceLocator::drawRegistrantId, 59 | CameraLatticeInfluenceDrawOverride::Creator); 60 | if (!status) { 61 | status.perror("tcCameraLatticeInfluenceLocator failed registerDrawOverrideCreator"); 62 | return status; 63 | } 64 | 65 | MString addMenu; 66 | addMenu += 67 | "global proc loadTcCameraLattice()\ 68 | {\ 69 | python(\"from tcCameraLattice import tcCameraLattice\\ntcCameraLattice.run()\");\ 70 | }\ 71 | \ 72 | global proc addTcCameraLatticeToShelf()\ 73 | {\ 74 | global string $gShelfTopLevel;\ 75 | \ 76 | string $shelves[] = `tabLayout - q - childArray $gShelfTopLevel`;\ 77 | string $shelfName = \"\";\ 78 | int $shelfFound = 0;\ 79 | for ($shelfName in $shelves)\ 80 | {\ 81 | if ($shelfName == \"Toolchefs\")\ 82 | {\ 83 | $shelfFound = 1;\ 84 | }\ 85 | }\ 86 | if ($shelfFound == 0)\ 87 | {\ 88 | addNewShelfTab \"Toolchefs\";\ 89 | }\ 90 | \ 91 | string $buttons[] = `shelfLayout -q -childArray \"Toolchefs\"`;\ 92 | int $buttonExists = 0;\ 93 | for ($button in $buttons)\ 94 | {\ 95 | string $lab = `shelfButton - q - label $button`;\ 96 | if ($lab == \"tcCameraLattice\")\ 97 | {\ 98 | $buttonExists = 1;\ 99 | break;\ 100 | }\ 101 | }\ 102 | \ 103 | if ($buttonExists == 0)\ 104 | {\ 105 | string $myButton = `shelfButton\ 106 | -parent Toolchefs\ 107 | -enable 1\ 108 | -width 34\ 109 | -height 34\ 110 | -manage 1\ 111 | -visible 1\ 112 | -annotation \"Load tcCameraLattice\"\ 113 | -label \"tcCameraLattice\"\ 114 | -image1 \"tcCameraLattice.png\"\ 115 | -style \"iconOnly\"\ 116 | -sourceType \"python\"\ 117 | -command \"from tcCameraLattice import tcCameraLattice\\ntcCameraLattice.run()\" tcCameraLatticeShelfButton`;\ 118 | }\ 119 | }\ 120 | global proc addTcCameraLatticeToMenu()\ 121 | {\ 122 | global string $gMainWindow;\ 123 | global string $showToolochefsMenuCtrl;\ 124 | if (!(`menu - exists $showToolochefsMenuCtrl`))\ 125 | {\ 126 | string $name = \"Toolchefs\";\ 127 | $showToolochefsMenuCtrl = `menu -p $gMainWindow -to true -l $name`;\ 128 | string $tcToolsMenu = `menuItem -subMenu true -label \"Tools\" -p $showToolochefsMenuCtrl \"tcToolsMenu\"`;\ 129 | menuItem -label \"Load tcCameraLattice\" -p $tcToolsMenu -c \"loadTcCameraLattice\" \"tcActiveCameraLatticeItem\";\ 130 | }\ 131 | else\ 132 | {\ 133 | int $deformerMenuExist = false;\ 134 | string $defMenu = \"\";\ 135 | string $subitems[] = `menu -q -itemArray $showToolochefsMenuCtrl`;\ 136 | for ($item in $subitems)\ 137 | {\ 138 | if ($item == \"tcToolsMenu\")\ 139 | {\ 140 | $deformerMenuExist = true;\ 141 | $defMenu = $item;\ 142 | break;\ 143 | }\ 144 | }\ 145 | if (!($deformerMenuExist))\ 146 | {\ 147 | string $tcToolsMenu = `menuItem -subMenu true -label \"Tools\" -p $showToolochefsMenuCtrl \"tcToolsMenu\"`;\ 148 | menuItem -label \"Load tcCameraLattice\" -p $tcToolsMenu -c \"loadTcCameraLattice\" \"tcActiveCameraLatticeItem\";\ 149 | }\ 150 | else\ 151 | {\ 152 | string $subitems2[] = `menu -q -itemArray \"tcToolsMenu\"`;\ 153 | int $deformerExists = 0;\ 154 | for ($item in $subitems2)\ 155 | {\ 156 | if ($item == \"tcActiveCameraLatticeItem\")\ 157 | {\ 158 | $deformerExists = true;\ 159 | break;\ 160 | }\ 161 | }\ 162 | if (!$deformerExists)\ 163 | {\ 164 | menuItem -label \"Load tcCameraLattice\" -p $defMenu -c \"loadTcCameraLattice\" \"tcActiveCameraLatticeItem\";\ 165 | }\ 166 | }\ 167 | }\ 168 | };addTcCameraLatticeToMenu();addTcCameraLatticeToShelf();"; 169 | MGlobal::executeCommand(addMenu, false, false); 170 | 171 | return status; 172 | } 173 | 174 | MStatus uninitializePlugin( MObject obj) 175 | { 176 | MStatus status = MStatus::kSuccess; 177 | MFnPlugin plugin( obj ); 178 | status = plugin.deregisterNode( CameraLattice::id ); 179 | if (!status) 180 | { 181 | MGlobal::displayError("Error deregistering node tcCameraLatticeDeformer"); 182 | return status; 183 | } 184 | 185 | status = plugin.deregisterNode( CameraLatticeTranslator::id ); 186 | if (!status) 187 | { 188 | MGlobal::displayError("Error deregistering node tcCameraLatticeTranslator"); 189 | return status; 190 | } 191 | 192 | status = MHWRender::MDrawRegistry::deregisterDrawOverrideCreator( 193 | CameraLatticeInfluenceLocator::drawDbClassification, 194 | CameraLatticeInfluenceLocator::drawRegistrantId); 195 | if (!status) { 196 | status.perror("Error deregistering drawOverrideCreator for tcCameraLatticeInfluenceAreaLocator"); 197 | return status; 198 | } 199 | 200 | status = plugin.deregisterNode( CameraLatticeInfluenceLocator::id ); 201 | if (!status) { 202 | status.perror("Error deregistering node tcCameraLatticeInfluenceAreaLocator"); 203 | return status; 204 | } 205 | 206 | 207 | return status; 208 | } 209 | -------------------------------------------------------------------------------- /source/cameraLattice.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | #include 38 | 39 | #include "cameraLattice.h" 40 | 41 | 42 | double fac(int n) 43 | { 44 | if (n == 0 || n == 1) 45 | return 1; 46 | else 47 | return n * fac(n-1) ; 48 | } 49 | 50 | double C(int i, int l) 51 | { 52 | return fac(l) / (fac(i) * fac(l - i)); 53 | } 54 | 55 | double B(int i, int l, double s) 56 | { 57 | return C(i, l) * pow(s,i) * pow(1-s,l-i); 58 | } 59 | 60 | void findBoundaryCells(const double w, const int D, int &min, int &max) 61 | { 62 | double step = 1.0/(D - 1); 63 | 64 | for (int i = 0; i < D - 1; i++) 65 | { 66 | double thisStep = step * i; 67 | if ((w < thisStep + step) && (w >= thisStep)) 68 | { 69 | min = i; 70 | max = i+1; 71 | return; 72 | } 73 | } 74 | 75 | //in case we are outside the case (LEFT) 76 | if (w < 0) 77 | { 78 | min = 0; 79 | max = 1; 80 | return; 81 | } 82 | 83 | //in case we are outside the case (RIGHT) 84 | if (D > 2) 85 | min = D - 2; 86 | else 87 | min = D - 1; 88 | 89 | max = D - 1; 90 | } 91 | 92 | void findLinearDeformedPoint(MPoint &tempPoint, const MPointArray *planePoints, const float u, const float v, const int sD, const int tD) 93 | { 94 | int minX, maxX, minY, maxY; 95 | findBoundaryCells(u, sD, minX, maxX); 96 | findBoundaryCells(v, tD, minY, maxY); 97 | 98 | //Finding projection factor on delimiting edges 99 | int factorX = sD - 1; 100 | int factorY = tD - 1; 101 | float uLocal = (u - float(minX) / factorX) / (float(maxX) / factorX - float(minX) / factorX); 102 | float vLocal = (v - float(minY) / factorY) / (float(maxY) / factorY - float(minY) / factorY); 103 | 104 | //find the edges --- aligned on the Y 105 | MPoint p1 = (*planePoints)[minX + minY * sD]; 106 | MPoint p2 = (*planePoints)[minX + maxY * sD]; 107 | MPoint p3 = (*planePoints)[maxX + minY * sD]; 108 | MPoint p4 = (*planePoints)[maxX + maxY * sD]; 109 | 110 | MPoint p21 = (p2 - p1) * vLocal + p1; 111 | MPoint p43 = (p4 - p3) * vLocal + p3; 112 | 113 | //finding the point between the edges -- this is on the X 114 | tempPoint = (p43 - p21) * uLocal + p21; 115 | } 116 | 117 | void findBezierDeformedPoint(MPoint &tempPoint, const MPointArray *planePoints, const float u, const float v, const int offsetS, const int offsetT, const int finalS, const int finalT, const int sD) 118 | { 119 | MVector result, temp; 120 | 121 | for ( unsigned s = 0; s < finalS; s++) 122 | for ( unsigned t = 0; t < finalT; t++) 123 | { 124 | int index = offsetS + s + (offsetT + t) * sD; 125 | result += (*planePoints)[index] * B(s, finalS - 1, u) * B(t, finalT - 1, v); 126 | } 127 | tempPoint = result; 128 | } 129 | 130 | double get_influencers_weight(const MPoint &pt, const std::vector *influencers) 131 | { 132 | MVector vec; 133 | 134 | double totalWeight = 0.0; 135 | for (unsigned int i = 0; i < influencers->size(); ++i) 136 | { 137 | const Influencer &influencer = (*influencers)[i]; 138 | vec = pt - influencer.pos; 139 | if (vec.length() > influencer.maxAxisLength) 140 | continue; 141 | 142 | vec = pt * (*influencers)[i].invMat; 143 | 144 | double length = vec.length(); 145 | //the radius of the locator in local space is 1 146 | if (length < 1) 147 | { 148 | if (length <= 0.0001 || influencer.falloff < 0.0001 || length < 1 - influencer.falloff) 149 | totalWeight = 1; 150 | else 151 | totalWeight += 1 - (length - (1 - influencer.falloff)) / influencer.falloff; 152 | } 153 | 154 | if (totalWeight >= 0.9999) 155 | return 1.0; 156 | } 157 | 158 | return totalWeight; 159 | } 160 | 161 | /********************************************************** 162 | CAMERA LATTICE DATA CLASS FOR TBB 163 | **********************************************************/ 164 | 165 | void CameraLatticeData::operator()( const tbb::blocked_range& r ) const 166 | { 167 | for( size_t i=r.begin(); i!=r.end(); ++i ) 168 | { 169 | MPoint intialPosition = (*m_data.points)[i]; 170 | 171 | double weight = m_data.envelopeValue; 172 | if ((*m_data.influencers).size() != 0) 173 | weight = m_data.envelopeValue * get_influencers_weight(intialPosition * (*m_data.toWorldMatrix), m_data.influencers); 174 | 175 | if (weight < 0.00001) 176 | continue; 177 | 178 | MPoint pt = intialPosition * *m_data.projectionMatrix; 179 | 180 | double zDepth = -pt[2]; 181 | 182 | if (!m_data.isOrtho) 183 | pt = pt / zDepth; 184 | 185 | double u = pt.x / m_data.filmHAperture + 0.5; 186 | double v = pt.y / m_data.filmVAperture + 0.5; 187 | 188 | double gov = m_data.gateOffsetValue; 189 | if (u > 1.0 + gov || v > 1.0 + gov || u < 0.0 - gov || v < 0.0 - gov) 190 | continue; 191 | 192 | MPoint finalPoint; 193 | if (m_data.behaviour == 1) 194 | { 195 | // remapping the u and v 196 | int minX, maxX, minY, maxY; 197 | findBoundaryCells(u, m_data.sD, minX, maxX); 198 | findBoundaryCells(v, m_data.tD, minY, maxY); 199 | 200 | minX = minX - m_data.maxRecursion < 0 ? 0 : minX - m_data.maxRecursion; 201 | maxX = maxX + m_data.maxRecursion > m_data.sD ? m_data.sD: maxX + m_data.maxRecursion; 202 | minY = minY - m_data.maxRecursion < 0 ? 0 : minY - m_data.maxRecursion; 203 | maxY = maxY + m_data.maxRecursion > m_data.tD ? m_data.tD: maxY + m_data.maxRecursion; 204 | 205 | float minSU = float(minX) / (m_data.sD - 1); float maxSU = float(maxX - 1) / (m_data.sD - 1); 206 | u = (u - minSU) / (maxSU - minSU); 207 | 208 | double minTU = double(minY) / (m_data.tD - 1); double maxTU = double(maxY - 1) / (m_data.tD - 1); 209 | v = (v - minTU) / (maxTU - minTU); 210 | 211 | findBezierDeformedPoint(finalPoint, m_data.planePoints, u, v, minX, minY, maxX - minX, maxY - minY, m_data.sD); 212 | } 213 | else 214 | findLinearDeformedPoint(finalPoint, m_data.planePoints, u, v, m_data.sD, m_data.tD); 215 | 216 | //we map it back to the (-1,1) range 217 | finalPoint.x *= m_data.filmHAperture; 218 | finalPoint.y *= m_data.filmVAperture; 219 | finalPoint.z = pt.z; 220 | 221 | if (!m_data.isOrtho) 222 | finalPoint = finalPoint * zDepth; 223 | 224 | finalPoint *= *m_data.invProjectionMatrix; 225 | if (weight > 0.9999) 226 | (*m_data.deformedPoints)[i] = finalPoint; 227 | else 228 | (*m_data.deformedPoints)[i] = intialPosition + (finalPoint - intialPosition) * weight; 229 | 230 | } 231 | }; 232 | 233 | 234 | /********************************************************** 235 | CAMERA LATTICE DEFORMER 236 | **********************************************************/ 237 | 238 | MTypeId CameraLattice::id( 0x00122C01 ); 239 | 240 | // local attributes 241 | // 242 | MObject CameraLattice::inputLattice; 243 | MObject CameraLattice::interpolation; 244 | MObject CameraLattice::objectMatrix; 245 | MObject CameraLattice::deformerMessage; 246 | MObject CameraLattice::latticeToDeformerMessage; 247 | MObject CameraLattice::sSubidivision; 248 | MObject CameraLattice::tSubidivision; 249 | MObject CameraLattice::cameraMatrix; 250 | MObject CameraLattice::inOrtho; 251 | MObject CameraLattice::inOrthographicWidth; 252 | MObject CameraLattice::inVerticalFilmAperture; 253 | MObject CameraLattice::inHorizontalFilmAperture; 254 | MObject CameraLattice::inFocalLength; 255 | MObject CameraLattice::maxBezierRecursion; 256 | MObject CameraLattice::influenceFalloff; 257 | MObject CameraLattice::influenceMatrix; 258 | MObject CameraLattice::gateOffset; 259 | 260 | 261 | CameraLattice::CameraLattice() 262 | { 263 | refreshLogicalIndex = true; 264 | cachedLogicalIndex.clear(); 265 | } 266 | 267 | CameraLattice::~CameraLattice() {} 268 | 269 | void* CameraLattice::creator() 270 | { 271 | return new CameraLattice(); 272 | } 273 | 274 | MStatus CameraLattice::initialize() 275 | { 276 | MFnMessageAttribute msgAttr; 277 | deformerMessage = msgAttr.create("deformerMessage", "dm"); 278 | latticeToDeformerMessage = msgAttr.create("ldMessage", "ldm"); 279 | 280 | MFnTypedAttribute tAttr; 281 | inputLattice = tAttr.create( "inputLattice", "il", MFnMeshData::kMesh ); 282 | tAttr.setStorable( false ); 283 | tAttr.setHidden( true ); 284 | 285 | MFnMatrixAttribute mAttr; 286 | objectMatrix = mAttr.create( "objectMatrix", "om"); 287 | mAttr.setHidden( true ); 288 | 289 | cameraMatrix = mAttr.create( "cameraMatrix", "cm"); 290 | mAttr.setHidden( true ); 291 | 292 | influenceMatrix = mAttr.create("influenceMatrix", "im"); 293 | mAttr.setHidden( true ); 294 | mAttr.setArray(true); 295 | 296 | MFnNumericAttribute nAttr; 297 | sSubidivision = nAttr.create( "sSubdivision", "ss", MFnNumericData::kLong); 298 | nAttr.setWritable(true); 299 | nAttr.setDefault(0); 300 | 301 | tSubidivision = nAttr.create( "tSubdivision", "ts", MFnNumericData::kLong); 302 | nAttr.setWritable(true); 303 | nAttr.setDefault(0); 304 | 305 | maxBezierRecursion = nAttr.create( "maxBezierRecursion", "mbr", MFnNumericData::kLong); 306 | nAttr.setWritable(true); 307 | nAttr.setDefault(10); 308 | 309 | MFnEnumAttribute enumAttr; 310 | interpolation = enumAttr.create("interpolation", "i", 0); 311 | enumAttr.addField("Linear", 0); 312 | enumAttr.addField("Bezier", 1); 313 | 314 | gateOffset = nAttr.create( "gateOffset", "go", MFnNumericData::kDouble); 315 | nAttr.setDefault(0.05); 316 | nAttr.setChannelBox(true); 317 | nAttr.setMin(0); 318 | nAttr.setMax(1); 319 | 320 | inFocalLength = nAttr.create( "inFocalLength", "iFL", MFnNumericData::kDouble); 321 | nAttr.setDefault(0); 322 | 323 | inHorizontalFilmAperture = nAttr.create( "inHorizontalFilmAperture", "iHF", MFnNumericData::kDouble); 324 | nAttr.setDefault(0); 325 | 326 | inVerticalFilmAperture = nAttr.create( "inVerticalFilmAperture", "iVF", MFnNumericData::kDouble); 327 | nAttr.setDefault(0); 328 | 329 | inOrthographicWidth = nAttr.create( "inOrthographicWidth", "iOW", MFnNumericData::kDouble); 330 | nAttr.setDefault(0); 331 | 332 | inOrtho = nAttr.create("inOrtho", "iO", MFnNumericData::kBoolean); 333 | nAttr.setDefault(false); 334 | 335 | influenceFalloff = nAttr.create("influenceFalloff", "iF", MFnNumericData::kDouble); 336 | nAttr.setDefault(0); 337 | nAttr.setArray(true); 338 | 339 | // deformation attributes 340 | addAttribute(deformerMessage); 341 | addAttribute(latticeToDeformerMessage); 342 | addAttribute(inputLattice); 343 | addAttribute(objectMatrix); 344 | addAttribute(cameraMatrix); 345 | addAttribute(sSubidivision); 346 | addAttribute(tSubidivision); 347 | addAttribute(interpolation); 348 | addAttribute(inOrtho); 349 | addAttribute(inOrthographicWidth); 350 | addAttribute(inVerticalFilmAperture); 351 | addAttribute(inHorizontalFilmAperture); 352 | addAttribute(inFocalLength); 353 | addAttribute(maxBezierRecursion); 354 | addAttribute(influenceMatrix); 355 | addAttribute(influenceFalloff); 356 | addAttribute(gateOffset); 357 | 358 | attributeAffects(inputLattice, CameraLattice::outputGeom); 359 | attributeAffects(objectMatrix, CameraLattice::outputGeom); 360 | attributeAffects(cameraMatrix, CameraLattice::outputGeom); 361 | attributeAffects(interpolation, CameraLattice::outputGeom); 362 | attributeAffects(inOrtho, CameraLattice::outputGeom); 363 | attributeAffects(inOrthographicWidth, CameraLattice::outputGeom); 364 | attributeAffects(inVerticalFilmAperture, CameraLattice::outputGeom); 365 | attributeAffects(inHorizontalFilmAperture, CameraLattice::outputGeom); 366 | attributeAffects(inFocalLength, CameraLattice::outputGeom); 367 | attributeAffects(CameraLattice::maxBezierRecursion, CameraLattice::outputGeom); 368 | attributeAffects(CameraLattice::influenceMatrix, CameraLattice::outputGeom); 369 | attributeAffects(CameraLattice::influenceFalloff, CameraLattice::outputGeom); 370 | attributeAffects(CameraLattice::gateOffset, CameraLattice::outputGeom); 371 | 372 | return MStatus::kSuccess; 373 | } 374 | 375 | MStatus 376 | CameraLattice::deform( MDataBlock& block, 377 | MItGeometry& iter, 378 | const MMatrix& m, 379 | unsigned int multiIndex) 380 | // 381 | // Method: deform 382 | // 383 | // Description: Deform the point with a squash algorithm 384 | // 385 | // Arguments: 386 | // block : the datablock of the node 387 | // iter : an iterator for the geometry to be deformed 388 | // m : matrix to transform the point into world space 389 | // multiIndex : the index of the geometry that we are deforming 390 | // 391 | // 392 | { 393 | MStatus returnStatus; 394 | 395 | // Envelope data from the base class. 396 | // The envelope is simply a scale factor. 397 | // 398 | MDataHandle envData = block.inputValue(envelope, &returnStatus); 399 | if (MS::kSuccess != returnStatus) return returnStatus; 400 | float envelopeValue = envData.asFloat(); 401 | if (envelopeValue < 0.01) return returnStatus; 402 | 403 | bool isOrtho = block.inputValue(inOrtho).asBool(); 404 | double ortographicWidth = block.inputValue(inOrthographicWidth).asDouble(); 405 | 406 | double horizontalAperture = block.inputValue(inHorizontalFilmAperture).asDouble(); 407 | double verticalAperture = block.inputValue(inVerticalFilmAperture).asDouble(); 408 | double focalLength = block.inputValue(inFocalLength).asDouble(); 409 | double gateOffsetValue =block.inputValue(gateOffset).asDouble(); 410 | 411 | double filmHAperture, filmVAperture; 412 | if (isOrtho) 413 | { 414 | filmHAperture = ortographicWidth; 415 | filmVAperture = ortographicWidth; 416 | } 417 | else 418 | { 419 | // 0.03937 is the factor mm to inches 420 | // 57.29578 is the maya conversion factor 421 | double hFov = 57.29578 * 2.0 * atan((0.5 * horizontalAperture) / (focalLength * 0.03937)); 422 | double vFov = 57.29578 * 2.0 * atan((0.5 * verticalAperture) / (focalLength * 0.03937)); 423 | 424 | //PLEASE NOTE: while the projected points which needs to be deformed are in a range (-1, 1), 425 | // but we want it to go between 0 and 1 to find the final deformation, that's the multiplication by 2 426 | 427 | // 3.14159265/180.f is the conversion to radians 428 | filmHAperture = tan((hFov*0.5) * 3.14159265 / 180.f) * 2; 429 | filmVAperture = tan((vFov*0.5) * 3.14159265 / 180.f) * 2; 430 | } 431 | 432 | std::vector influencers; 433 | MArrayDataHandle iFalloffArrayHandle = block.inputArrayValue(influenceFalloff); 434 | MArrayDataHandle iMatrixArrayHandle = block.inputArrayValue(influenceMatrix); 435 | int count = iFalloffArrayHandle.elementCount(); 436 | if (count != iMatrixArrayHandle.elementCount()) 437 | { 438 | MGlobal::displayWarning("tcCameraLatticeDeformer: something is wrong with your influence area connection. Ignoring influence areas."); 439 | } 440 | else 441 | { 442 | 443 | if (refreshLogicalIndex) 444 | { 445 | //we need to do this here as connectionMade and connectionBroken are called before the connections are made 446 | MPlug falloffPlug(thisMObject(), influenceFalloff); 447 | unsigned int numConnectedElements = falloffPlug.numConnectedElements(); 448 | cachedLogicalIndex.clear(); 449 | for (unsigned int i = 0; i < numConnectedElements; i++) 450 | { 451 | MPlug tempPlug = falloffPlug.connectionByPhysicalIndex(i); 452 | if (tempPlug.isSource()) continue; 453 | cachedLogicalIndex.append(tempPlug.logicalIndex()); 454 | } 455 | 456 | refreshLogicalIndex = false; 457 | } 458 | 459 | if (cachedLogicalIndex.length() > 0) 460 | { 461 | influencers.reserve(cachedLogicalIndex.length()); 462 | MVector vec; 463 | for (unsigned int i = 0; i < cachedLogicalIndex.length(); i++) 464 | { 465 | iFalloffArrayHandle.jumpToArrayElement(cachedLogicalIndex[i]); 466 | iMatrixArrayHandle.jumpToArrayElement(cachedLogicalIndex[i]); 467 | 468 | Influencer influencer; 469 | influencer.falloff = iFalloffArrayHandle.inputValue().asDouble(); 470 | MMatrix mat = iMatrixArrayHandle.inputValue().asMatrix(); 471 | influencer.invMat = mat.inverse(); 472 | 473 | influencer.pos.x = mat[3][0]; 474 | influencer.pos.y = mat[3][1]; 475 | influencer.pos.z = mat[3][2]; 476 | 477 | vec.x = mat[0][0]; 478 | vec.y = mat[0][1]; 479 | vec.z = mat[0][2]; 480 | influencer.maxAxisLength = vec.length(); 481 | 482 | vec.x = mat[1][0]; 483 | vec.y = mat[1][1]; 484 | vec.z = mat[1][2]; 485 | double thisLength = vec.length(); 486 | if (thisLength > influencer.maxAxisLength) 487 | influencer.maxAxisLength = thisLength; 488 | 489 | vec.x = mat[2][0]; 490 | vec.y = mat[2][1]; 491 | vec.z = mat[2][2]; 492 | thisLength = vec.length(); 493 | if (thisLength > influencer.maxAxisLength) 494 | influencer.maxAxisLength = thisLength; 495 | 496 | influencers.push_back(influencer); 497 | } 498 | } 499 | } 500 | 501 | 502 | int sD = block.inputValue(sSubidivision).asInt(); 503 | int tD = block.inputValue(tSubidivision).asInt(); 504 | 505 | int maxRecursion = block.inputValue(maxBezierRecursion).asInt(); 506 | 507 | MDataHandle inputLatticeHnd = block.inputValue(inputLattice); 508 | MFnMesh planeMesh(inputLatticeHnd.asMesh()); 509 | MPointArray planePoints; 510 | planeMesh.getPoints(planePoints); 511 | 512 | if (planePoints.length() == 0 || planePoints.length() != sD * tD) 513 | return MStatus::kFailure; 514 | 515 | int behaviour = block.inputValue(interpolation).asShort(); 516 | 517 | MDataHandle matData = block.inputValue(objectMatrix); 518 | MMatrix objMat = matData.asMatrix(); 519 | 520 | matData = block.inputValue(cameraMatrix); 521 | MMatrix camMat = matData.asMatrix(); 522 | 523 | MMatrix projectionMatrix = objMat * camMat.inverse(); 524 | MMatrix invProjectionMatrix = camMat * objMat.inverse(); 525 | 526 | MPointArray points, deformedPoints; 527 | iter.allPositions(points); 528 | 529 | deformedPoints.copy(points); 530 | 531 | CameraLatticeData dataObj(&projectionMatrix, &invProjectionMatrix, &objMat, &points, &deformedPoints, &planePoints, filmHAperture, filmVAperture, sD, tD, isOrtho, maxRecursion, behaviour, &influencers, gateOffsetValue, envelopeValue); 532 | tbb::parallel_for(tbb::blocked_range(0, points.length()), dataObj); 533 | 534 | iter.setAllPositions(deformedPoints); 535 | 536 | return MS::kSuccess; 537 | } 538 | 539 | MStatus CameraLattice::connectionMade (const MPlug &plug, const MPlug &otherPlug, bool asSrc) 540 | { 541 | if (plug == influenceFalloff || plug == influenceMatrix) 542 | { 543 | refreshLogicalIndex = true; 544 | } 545 | 546 | return MPxDeformerNode::connectionMade(plug, otherPlug, asSrc); 547 | } 548 | 549 | MStatus CameraLattice::connectionBroken (const MPlug &plug, const MPlug &otherPlug, bool asSrc) 550 | { 551 | if (plug == influenceFalloff || plug == influenceMatrix) 552 | { 553 | refreshLogicalIndex = true; 554 | } 555 | 556 | return MPxDeformerNode::connectionBroken(plug, otherPlug, asSrc); 557 | } 558 | 559 | 560 | 561 | 562 | -------------------------------------------------------------------------------- /source/cameraLatticeInfluenceLocator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // cameraLatticeInfluenceLocator.cpp 3 | // cameraLattice 4 | // 5 | // Created by Daniele Federico on 02/03/15. 6 | // 7 | // 8 | 9 | #define PI 3.14159265358979323846 10 | #define DEG2RAD(DEG) ((DEG)*((PI)/(180.0))) 11 | 12 | #include 13 | #include 14 | #include 15 | #include "cameraLatticeInfluenceLocator.h" 16 | 17 | MObject CameraLatticeInfluenceLocator::falloff; 18 | MObject CameraLatticeInfluenceLocator::message; 19 | MTypeId CameraLatticeInfluenceLocator::id( 0x00122C04 ); 20 | MString CameraLatticeInfluenceLocator::drawDbClassification("drawdb/geometry/cameraLatticeInfluenceArea"); 21 | MString CameraLatticeInfluenceLocator::drawRegistrantId("tcCameraLatticeInfluenceNodePlugin"); 22 | 23 | CameraLatticeInfluenceLocator::CameraLatticeInfluenceLocator() {} 24 | CameraLatticeInfluenceLocator::~CameraLatticeInfluenceLocator() {} 25 | 26 | MStatus CameraLatticeInfluenceLocator::compute( const MPlug& /*plug*/, MDataBlock& /*data*/ ) 27 | { 28 | return MS::kUnknownParameter; 29 | } 30 | 31 | void CameraLatticeInfluenceLocator::drawCircle(const int axis, const double radius) 32 | { 33 | glBegin(GL_LINE_LOOP); 34 | 35 | for (int i=0; i < 360; i+=4) 36 | { 37 | float degInRad = DEG2RAD(i); 38 | if (axis == 0) 39 | glVertex3f(cos(degInRad)*radius, sin(degInRad)*radius, 0); 40 | else if (axis == 1) 41 | glVertex3f(cos(degInRad)*radius, 0, sin(degInRad)*radius); 42 | else 43 | glVertex3f(0, cos(degInRad)*radius, sin(degInRad)*radius); 44 | } 45 | 46 | glEnd(); 47 | } 48 | 49 | #if MAYA_API_VERSION < 201900 // obsolete for Maya 2019 version and higher 50 | // called by legacy default viewport 51 | void CameraLatticeInfluenceLocator::draw( M3dView & view, const MDagPath &path, 52 | M3dView::DisplayStyle style, 53 | M3dView::DisplayStatus status ) 54 | { 55 | if (status == M3dView::kInvisible) 56 | return; 57 | 58 | MPlug plug( thisMObject(), falloff ); 59 | double falloffVal; 60 | plug.getValue( falloffVal ); 61 | 62 | view.beginGL(); 63 | 64 | view.setDrawColor(MHWRender::MGeometryUtilities::wireframeColor(path)); 65 | 66 | /* 67 | if (status == M3dView::kActive || status == M3dView::kLead) 68 | view.setDrawColor( MColor( 0.8f, 1.0f, 0.8f, 1.0f ) ); 69 | else 70 | view.setDrawColor( MColor( 0.0f, 0.0f, 0.3f, 1.0f ) ); 71 | */ 72 | 73 | drawCircle(0, 1); 74 | drawCircle(1, 1); 75 | drawCircle(2, 1); 76 | 77 | if (falloffVal < 0.02) 78 | falloffVal = 0.02; 79 | else if (falloffVal > 0.98) 80 | falloffVal = 0.98; 81 | falloffVal = 1 - falloffVal; 82 | 83 | view.setDrawColor( MColor( 1.0f, 0.0f, 0.0f, 0.5f ) ); 84 | drawCircle(0, falloffVal); 85 | drawCircle(1, falloffVal); 86 | drawCircle(2, falloffVal); 87 | 88 | 89 | view.endGL(); 90 | } 91 | #endif 92 | 93 | bool CameraLatticeInfluenceLocator::isBounded() const 94 | { 95 | return true; 96 | } 97 | 98 | MBoundingBox CameraLatticeInfluenceLocator::boundingBox() const 99 | { 100 | // Get the size 101 | // 102 | 103 | MPoint corner1( -1, -1, -1 ); 104 | MPoint corner2( 1, 1, 1 ); 105 | 106 | return MBoundingBox( corner1, corner2 ); 107 | } 108 | 109 | void* CameraLatticeInfluenceLocator::creator() 110 | { 111 | return new CameraLatticeInfluenceLocator(); 112 | } 113 | 114 | MStatus CameraLatticeInfluenceLocator::initialize() 115 | { 116 | MFnUnitAttribute unitFn; 117 | MStatus stat; 118 | 119 | MFnNumericAttribute nAttr; 120 | 121 | falloff = nAttr.create( "falloff", "fa", MFnNumericData::kDouble); 122 | nAttr.setWritable(true); 123 | nAttr.setDefault(0.5); 124 | nAttr.setMin(0); 125 | nAttr.setMax(1); 126 | nAttr.setKeyable(true); 127 | nAttr.setConnectable(true); 128 | 129 | stat = addAttribute(falloff); 130 | if (!stat) 131 | { 132 | stat.perror("Failed while adding falloff attribute."); 133 | return stat; 134 | } 135 | 136 | MFnMessageAttribute msgAttr; 137 | message = msgAttr.create("locatorMessage", "lm"); 138 | msgAttr.setArray(true); 139 | stat = addAttribute(message); 140 | if (!stat) 141 | { 142 | stat.perror("Failed while adding locatorMessage attribute."); 143 | return stat; 144 | } 145 | 146 | return MS::kSuccess; 147 | } 148 | 149 | //--------------------------------------------------------------------------- 150 | //--------------------------------------------------------------------------- 151 | // Viewport 2.0 override implementation 152 | //--------------------------------------------------------------------------- 153 | //--------------------------------------------------------------------------- 154 | 155 | 156 | CameraLatticeInfluenceDrawOverride::CameraLatticeInfluenceDrawOverride(const MObject& obj) 157 | : MHWRender::MPxDrawOverride(obj, NULL, false), 158 | cameraLatticeInfluenceLocator(obj) 159 | { 160 | fModelEditorChangedCbId = MEventMessage::addEventCallback( 161 | "modelEditorChanged", OnModelEditorChanged, this); 162 | } 163 | 164 | CameraLatticeInfluenceDrawOverride::~CameraLatticeInfluenceDrawOverride() 165 | { 166 | if (fModelEditorChangedCbId != 0) 167 | { 168 | MMessage::removeCallback(fModelEditorChangedCbId); 169 | fModelEditorChangedCbId = 0; 170 | } 171 | } 172 | 173 | void CameraLatticeInfluenceDrawOverride::OnModelEditorChanged(void *clientData) 174 | { 175 | // Mark the node as being dirty so that it can update on display mode switch, 176 | // e.g. between wireframe and shaded. 177 | CameraLatticeInfluenceDrawOverride *ovr = static_cast(clientData); 178 | if (ovr) MHWRender::MRenderer::setGeometryDrawDirty(ovr->cameraLatticeInfluenceLocator); 179 | } 180 | 181 | MHWRender::DrawAPI CameraLatticeInfluenceDrawOverride::supportedDrawAPIs() const 182 | { 183 | return MHWRender::kAllDevices; 184 | } 185 | 186 | 187 | bool CameraLatticeInfluenceDrawOverride::isBounded(const MDagPath& /*objPath*/, 188 | const MDagPath& /*cameraPath*/) const 189 | { 190 | return true; 191 | } 192 | 193 | MBoundingBox CameraLatticeInfluenceDrawOverride::boundingBox( 194 | const MDagPath& objPath, 195 | const MDagPath& cameraPath) const 196 | { 197 | MPoint corner1( -1, -1, -1 ); 198 | MPoint corner2( 1, 1, 1 ); 199 | 200 | CameraLatticeInfluenceDrawOverride *nonConstThis = (CameraLatticeInfluenceDrawOverride *)this; 201 | nonConstThis->mCurrentBoundingBox.clear(); 202 | nonConstThis->mCurrentBoundingBox.expand( corner1 ); 203 | nonConstThis->mCurrentBoundingBox.expand( corner2 ); 204 | 205 | return mCurrentBoundingBox; 206 | } 207 | 208 | bool CameraLatticeInfluenceDrawOverride::disableInternalBoundingBoxDraw() const 209 | { 210 | return false; 211 | } 212 | 213 | // Called by Maya each time the object needs to be drawn. 214 | MUserData* CameraLatticeInfluenceDrawOverride::prepareForDraw( 215 | const MDagPath& objPath, 216 | const MDagPath& cameraPath, 217 | const MHWRender::MFrameContext& frameContext, 218 | MUserData* oldData) 219 | { 220 | // Any data needed from the Maya dependency graph must be retrieved and cached in this stage. 221 | // There is one cache data for each drawable instance, if it is not desirable to allow Maya to handle data 222 | // caching, simply return null in this method and ignore user data parameter in draw callback method. 223 | // e.g. in this sample, we compute and cache the data for usage later when we create the 224 | // MUIDrawManager to draw CameraLatticeInfluence in method addUIDrawables(). 225 | CameraLatticeInfluenceData* data = dynamic_cast(oldData); 226 | if (!data) 227 | { 228 | data = new CameraLatticeInfluenceData(); 229 | data->center = MPoint(0,0,0); 230 | data->X = MVector(1,0,0); 231 | data->Y = MVector(0,1,0); 232 | data->Z = MVector(0,0,1); 233 | } 234 | 235 | 236 | MPlug plug( objPath.node(), CameraLatticeInfluenceLocator::falloff ); 237 | plug.getValue(data->falloffVal); 238 | if (data->falloffVal < 0.02) 239 | data->falloffVal = 0.02; 240 | else if (data->falloffVal > 0.98) 241 | data->falloffVal = 0.98; 242 | data->falloffVal = 1 - data->falloffVal; 243 | 244 | // get correct color based on the state of object, e.g. active or dormant 245 | data->color = MHWRender::MGeometryUtilities::wireframeColor(objPath); 246 | 247 | return data; 248 | } 249 | 250 | // addUIDrawables() provides access to the MUIDrawManager, which can be used 251 | // to queue up operations for drawing simple UI elements such as lines, circles and 252 | // text. To enable addUIDrawables(), override hasUIDrawables() and make it return true. 253 | void CameraLatticeInfluenceDrawOverride::addUIDrawables( 254 | const MDagPath& objPath, 255 | MHWRender::MUIDrawManager& drawManager, 256 | const MHWRender::MFrameContext& frameContext, 257 | const MUserData* data) 258 | { 259 | // Get data cached by prepareForDraw() for each drawable instance, then MUIDrawManager 260 | // can draw simple UI by these data. 261 | 262 | CameraLatticeInfluenceData* pLocatorData = (CameraLatticeInfluenceData*)data; 263 | if (!pLocatorData) 264 | { 265 | return; 266 | } 267 | 268 | // Insert your custom drawing part here 269 | drawManager.beginDrawable(); 270 | 271 | drawManager.setColor( pLocatorData->color ); 272 | #ifndef MAYA2014 273 | drawManager.setDepthPriority(5); 274 | #endif 275 | drawManager.circle( pLocatorData->center, pLocatorData->X, 1, false); 276 | drawManager.circle( pLocatorData->center, pLocatorData->Y, 1, false); 277 | drawManager.circle( pLocatorData->center, pLocatorData->Z, 1, false); 278 | 279 | 280 | if (pLocatorData->falloffVal > 0) 281 | { 282 | drawManager.setColor( MColor(1.0f, 0.0f, 0.0f, 1.0f)); 283 | drawManager.circle( pLocatorData->center, pLocatorData->X, pLocatorData->falloffVal, false); 284 | drawManager.circle( pLocatorData->center, pLocatorData->Y, pLocatorData->falloffVal, false); 285 | drawManager.circle( pLocatorData->center, pLocatorData->Z, pLocatorData->falloffVal, false); 286 | } 287 | 288 | drawManager.endDrawable(); 289 | } 290 | -------------------------------------------------------------------------------- /source/cameraLatticeTranslator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CameraLatticeTranslator.cpp 3 | * CameraLatticeTranslator 4 | * 5 | * 6 | */ 7 | 8 | #include "cameraLatticeTranslator.h" 9 | 10 | MTypeId CameraLatticeTranslator::id( 0x00122C02 ); 11 | 12 | MObject CameraLatticeTranslator::inNearClipPlane; 13 | MObject CameraLatticeTranslator::inFocalLength; 14 | MObject CameraLatticeTranslator::inHorizontalFilmAperture; 15 | MObject CameraLatticeTranslator::inVerticalFilmAperture; 16 | MObject CameraLatticeTranslator::outScaleX; 17 | MObject CameraLatticeTranslator::outScaleY; 18 | MObject CameraLatticeTranslator::outTranslateZ; 19 | MObject CameraLatticeTranslator::inOrtho; 20 | MObject CameraLatticeTranslator::inOrthographicWidth; 21 | 22 | void *CameraLatticeTranslator::creator() 23 | { 24 | return new CameraLatticeTranslator(); 25 | } 26 | 27 | MStatus CameraLatticeTranslator::compute( const MPlug& plug, MDataBlock& data ) 28 | { 29 | MStatus stat; 30 | 31 | if (plug == outScaleX || plug == outScaleY) 32 | { 33 | 34 | double nearClipPlane = data.inputValue(inNearClipPlane).asDouble(); 35 | double w = data.inputValue(inHorizontalFilmAperture).asDouble() * 25.4; //25.4 is the inch to meter factor, the film aperture is store in inches 36 | double h = data.inputValue(inVerticalFilmAperture).asDouble() * 25.4; 37 | double focalLength = data.inputValue(inFocalLength).asDouble(); 38 | 39 | bool isOrtho = data.inputValue(inOrtho).asBool(); 40 | double ortographicWidth = data.inputValue(inOrthographicWidth).asDouble(); 41 | 42 | double scaleX, scaleY; 43 | if (!isOrtho) 44 | { 45 | double wfov = 2.0 * atan(0.5 * w / focalLength ); 46 | double hfov = 2.0 * atan(0.5 * h / focalLength ); 47 | 48 | scaleX = 2.0 * tan( wfov / 2.0 ) * nearClipPlane; 49 | scaleY = 2.0 * tan( hfov / 2.0 ) * nearClipPlane; 50 | } 51 | else 52 | { 53 | scaleX = ortographicWidth; 54 | scaleY = ortographicWidth; 55 | } 56 | 57 | MDataHandle outScaleXHandle = data.outputValue(outScaleX); 58 | MDataHandle outScaleYHandle = data.outputValue(outScaleY); 59 | outScaleXHandle.setDouble(scaleX); 60 | outScaleYHandle.setDouble(scaleY); 61 | 62 | outScaleXHandle.setClean(); 63 | outScaleYHandle.setClean(); 64 | 65 | data.setClean( plug ); 66 | } 67 | else if (plug == outTranslateZ) 68 | { 69 | double nearClipPlane = data.inputValue(inNearClipPlane).asDouble(); 70 | MDataHandle outTranslateXHandle = data.outputValue(outTranslateZ); 71 | 72 | bool isOrtho = data.inputValue(inOrtho).asBool(); 73 | double offset = isOrtho ? 0.04: 0.0001; 74 | 75 | // we add an extra tiny offset so the lattice is visible 76 | outTranslateXHandle.setDouble(nearClipPlane * -1 - offset); 77 | outTranslateXHandle.setClean(); 78 | data.setClean(plug); 79 | } 80 | return stat; 81 | } 82 | 83 | 84 | MStatus CameraLatticeTranslator::initialize() 85 | { 86 | MFnNumericAttribute nAttr; 87 | 88 | outScaleX = nAttr.create( "outScaleX", "oSX", MFnNumericData::kDouble); 89 | nAttr.setWritable(false); 90 | nAttr.setDefault(0); 91 | outScaleY = nAttr.create( "outScaleY", "oSY", MFnNumericData::kDouble); 92 | nAttr.setWritable(false); 93 | nAttr.setDefault(0); 94 | 95 | outTranslateZ = nAttr.create( "outTranslateZ", "oTZ", MFnNumericData::kDouble); 96 | nAttr.setWritable(false); 97 | nAttr.setDefault(0); 98 | 99 | inNearClipPlane = nAttr.create( "inNearClipPlane", "iNC", MFnNumericData::kDouble); 100 | nAttr.setDefault(0); 101 | 102 | inFocalLength = nAttr.create( "inFocalLength", "iFL", MFnNumericData::kDouble); 103 | nAttr.setDefault(0); 104 | 105 | inHorizontalFilmAperture = nAttr.create( "inHorizontalFilmAperture", "iHF", MFnNumericData::kDouble); 106 | nAttr.setDefault(0); 107 | 108 | inVerticalFilmAperture = nAttr.create( "inVerticalFilmAperture", "iVF", MFnNumericData::kDouble); 109 | nAttr.setDefault(0); 110 | 111 | inOrthographicWidth = nAttr.create( "inOrthographicWidth", "iOW", MFnNumericData::kDouble); 112 | nAttr.setDefault(0); 113 | 114 | inOrtho = nAttr.create("inOrtho", "iO", MFnNumericData::kBoolean); 115 | nAttr.setDefault(false); 116 | 117 | addAttribute(outScaleX ); 118 | addAttribute(outScaleY ); 119 | addAttribute(outTranslateZ ); 120 | addAttribute(inNearClipPlane ); 121 | addAttribute(inFocalLength ); 122 | addAttribute(inHorizontalFilmAperture ); 123 | addAttribute(inVerticalFilmAperture ); 124 | addAttribute(inOrtho ); 125 | addAttribute(inOrthographicWidth ); 126 | 127 | attributeAffects(inNearClipPlane, outScaleX ); 128 | attributeAffects(inFocalLength, outScaleX ); 129 | attributeAffects(inHorizontalFilmAperture, outScaleX ); 130 | attributeAffects(inVerticalFilmAperture, outScaleX ); 131 | attributeAffects(inNearClipPlane, outScaleY ); 132 | attributeAffects(inFocalLength, outScaleY ); 133 | attributeAffects(inHorizontalFilmAperture, outScaleY ); 134 | attributeAffects(inVerticalFilmAperture, outScaleY ); 135 | 136 | attributeAffects(inOrtho, outScaleX ); 137 | attributeAffects(inOrtho, outScaleY ); 138 | 139 | attributeAffects(inOrthographicWidth, outScaleX ); 140 | attributeAffects(inOrthographicWidth, outScaleY ); 141 | 142 | attributeAffects(inOrtho, outTranslateZ); 143 | attributeAffects(inNearClipPlane, outTranslateZ ); 144 | 145 | return MS::kSuccess; 146 | } 147 | --------------------------------------------------------------------------------