/// <summary> /// rotate vector with quaternion /// </summary> /// <param name="v"></param> /// <param name="q"></param> /// <returns> /// the rotated vector /// </returns> private IPoint3 RotateVectorByQuaternion(IPoint3 v, IQuat q) { var qx = q.X; var qy = q.Y; var qz = q.Z; var qw = q.W; // compute rotation matrix from q // see: https://www.mathworks.com/help/aerotbx/ug/quatrotate.html var m11 = 1 - 2 * qy * qy - 2 * qz * qz; var m12 = 2 * (qx * qy + qw * qz); var m13 = 2 * (qx * qz - qw * qy); var m21 = 2 * (qx * qy - qw * qz); var m22 = 1 - 2 * qx * qx - 2 * qz * qz; var m23 = 2 * (qy * qz + qw * qx); var m31 = 2 * (qx * qz + qw * qy); var m32 = 2 * (qy * qz - qw * qx); var m33 = 1 - 2 * qx * qx - 2 * qy * qy; // matrix multiplication var vx_rot = m11 * v.X + m12 * v.Y + m13 * v.Z; var vy_rot = m21 * v.X + m22 * v.Y + m23 * v.Z; var vz_rot = m31 * v.X + m32 * v.Y + m33 * v.Z; return(Loader.Global.Point3.Create(vx_rot, vy_rot, vz_rot)); }
private static bool ExportQuaternionController(IControl control, string property, List <BabylonAnimation> animations) { IQuat previousQuat = null; return(ExportController(control, property, animations, 0x2003, BabylonAnimation.DataType.Quaternion, (index, keyControl) => { var key = Loader.Global.ILinRotKey.Create(); keyControl.GetKey(index, key); var newQuat = key.Val; if (index > 0) { newQuat = previousQuat.Multiply(newQuat); } previousQuat = newQuat; return new BabylonAnimationKey { frame = key.Time / Loader.Global.TicksPerFrame, values = newQuat.ToArray() }; })); }
private IMatrix3 GetOffsetTM(IIGameNode gameNode, int key) { IPoint3 objOffsetPos = gameNode.MaxNode.ObjOffsetPos; IQuat objOffsetQuat = gameNode.MaxNode.ObjOffsetRot; IPoint3 objOffsetScale = gameNode.MaxNode.ObjOffsetScale.S; // conversion: LH vs RH coordinate system (swap Y and Z) var tmpSwap = objOffsetPos.Y; objOffsetPos.Y = objOffsetPos.Z; objOffsetPos.Z = tmpSwap; tmpSwap = objOffsetQuat.Y; objOffsetQuat.Y = objOffsetQuat.Z; objOffsetQuat.Z = tmpSwap; var objOffsetRotMat = Tools.Identity; objOffsetQuat.MakeMatrix(objOffsetRotMat, true); tmpSwap = objOffsetScale.Y; objOffsetScale.Y = objOffsetScale.Z; objOffsetScale.Z = tmpSwap; // build the offset transform; equivalent in maxscript: // offsetTM = (scaleMatrix $.objectOffsetScale) * ($.objectOffsetRot as matrix3) * (transMatrix $.objectOffsetPos) IMatrix3 offsetTM = Tools.Identity; offsetTM.Scale(objOffsetScale, false); offsetTM.MultiplyBy(objOffsetRotMat); offsetTM.Translate(objOffsetPos); return(offsetTM); }
private void RotationToEulerAngles(BabylonAbstractMesh babylonMesh, IQuat rotation) { float rotx = 0, roty = 0, rotz = 0; unsafe { rotation.GetEuler(new IntPtr(&rotx), new IntPtr(&roty), new IntPtr(&rotz)); } babylonMesh.rotation = new[] { rotx, roty, rotz }; }
private float[] QuaternionToEulerAngles(IQuat rotation) { float rotx = 0, roty = 0, rotz = 0; unsafe { rotation.GetEuler(new IntPtr(&rotx), new IntPtr(&roty), new IntPtr(&rotz)); } return(new[] { rotx, roty, rotz }); }
public static Vector3 ToEulerAngles(this IQuat q) { // Store the Euler angles in radians var pitchYawRoll = new Vector3(); double sqw = q.W * q.W; double sqx = q.X * q.X; double sqy = q.Y * q.Y; double sqz = q.Z * q.Z; // If quaternion is normalised the unit is one, otherwise it is the correction factor double unit = sqx + sqy + sqz + sqw; double test = q.X * q.Y + q.Z * q.W; if (test > 0.4999f * unit) // 0.4999f OR 0.5f - EPSILON { // Singularity at north pole pitchYawRoll.Y = 2f * (float)Math.Atan2(q.X, q.W); // Yaw pitchYawRoll.X = (float)Math.PI * 0.5f; // Pitch pitchYawRoll.Z = 0f; // Roll return(pitchYawRoll); } if (test < -0.4999f * unit) // -0.4999f OR -0.5f + EPSILON { // Singularity at south pole pitchYawRoll.Y = -2f * (float)Math.Atan2(q.X, q.W); // Yaw pitchYawRoll.X = -(float)Math.PI * 0.5f; // Pitch pitchYawRoll.Z = 0f; // Roll return(pitchYawRoll); } pitchYawRoll.Y = (float)Math.Atan2(2f * q.Y * q.W - 2f * q.X * q.Z, sqx - sqy - sqz + sqw); // Yaw pitchYawRoll.X = (float)Math.Asin(2f * test / unit); // Pitch pitchYawRoll.Z = (float)Math.Atan2(2f * q.X * q.W - 2f * q.Y * q.Z, -sqx + sqy - sqz + sqw); // Roll return(pitchYawRoll); }
public static float[] GetRotation(IINode node, IINode renderedNode) { float[] res = new float[4]; IMatrix3 nodeTm = node.GetNodeTM(0, Tools.Forever); IMatrix3 inverted = renderedNode.GetNodeTM(0, Tools.Forever); inverted.Invert(); nodeTm = nodeTm.Multiply(inverted); IPoint3 p = Loader.Global.Point3.Create(0, 0, 0); IQuat q = Loader.Global.IdentQuat; IPoint3 s = Loader.Global.Point3.Create(0, 0, 0); Loader.Global.DecomposeMatrix(nodeTm, p, q, s); q.Normalize(); res[0] = q[0]; res[1] = q[2]; res[2] = -q[1]; res[3] = -q[3]; return(res); }
public static float[] ToArray(this IQuat value) { return(new[] { value.X, value.Z, value.Y, value.W }); }
public static Quaternion ToQuat(this IQuat value) { return(new Quaternion(value.X, value.Z, value.Y, value.W)); }
public static Quaternion convertTo(this IQuat _input) { return(new Quaternion(_input.X, _input.Y, _input.Z, _input.W)); }
/// <summary> /// This is the routine to convert the input node to polygon faces. /// </summary> /// <param name="nodeHandle"> Input the node by handle. </param> /// <param name="convertToTri"> Input whether to convert to a poly object first. </param> /// <param name="addShell"> Input whether to add the shell modifier when finished converting to face. </param> /// <param name="shell"> Input the shell thickness amount. </param> /// <param name="addEditMesh"> Input whether to add the Edit Mesh modifier when finished converting to face. </param> /// <param name="collapseNode"> Input whether to collapse the node afterwards. </param> /// <param name="centerPivot"> Input whether to center the pivot on each new face. </param> /// <returns> Returns 1 if successful or -1 if not. </returns> static public int ConvertToPolygonFaces(uint nodeHandle, bool convertToPoly = true, // C# now supports default parameters bool addShell = true, float shell = 0.1f, bool addEditMesh = true, bool collapseNode = true, bool centerPivot = true) { try { IGlobal global = Autodesk.Max.GlobalInterface.Instance; IInterface14 ip = global.COREInterface14; IINode node = ip.GetINodeByHandle(nodeHandle); if (node == null) { return(-1); } // Get it's current object state. If a modifier has been applied, for example, // it is going to return the OS of the mesh in it's current form in the timeline. IObjectState os = node.ObjectRef.Eval(ip.Time); // Now grab the object itself. IObject objOriginal = os.Obj; IPolyObject polyObject = objOriginal as IPolyObject; IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.POLYOBJ_CLASS_ID, 0); IPolyObject polyObj = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID) as IPolyObject; if (polyObject == null && convertToPoly) { if (objOriginal.CanConvertToType(global.TriObjectClassID) == 1) { objOriginal = objOriginal.ConvertToType(ip.Time, global.TriObjectClassID); } else { return(-1); } ITriObject triOriginal = objOriginal as ITriObject; polyObj.Mesh.AddTri(triOriginal.Mesh); polyObj.Mesh.FillInMesh(); polyObj.Mesh.EliminateBadVerts(0); polyObj.Mesh.MakePolyMesh(0, true); } else if (polyObject == null) { polyObj = polyObject; } else { return(-1); } IMatrix3 mat = node.GetNodeTM(0, null); IPoint3 ptOffsetPos = node.ObjOffsetPos; IQuat quatOffsetRot = node.ObjOffsetRot; IScaleValue scaleOffsetScale = node.ObjOffsetScale; // We can grab the faces as a List and iterate them in .NET API. int nNumFaces = polyObj.Mesh.FNum; if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressMaxNum = nNumFaces; } ADN_UserBreakCheck checkUserBreak = new ADN_UserBreakCheck(); for (int i = 0; i < nNumFaces; i++) { if (checkUserBreak.Check() == true) { return(-1); } if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressCurrNum = i; } // Create a new poly object for each new face. object objectNewFace = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID); // Create a new node to hold it in the scene. IObject objNewFace = (IObject)objectNewFace; IINode n = global.COREInterface.CreateObjectNode(objNewFace); // Name it and ensure it is unique... string newname = "ADN-Sample-Face"; ip.MakeNameUnique(ref newname); n.Name = newname; // Based on what we created above, we can safely cast it to TriObject IPolyObject polyNewFace = objNewFace as IPolyObject; // Setup the new poly object with 1 face, and the vertex count from the original object's face we are processing polyNewFace.Mesh.SetNumFaces(1); polyNewFace.Mesh.SetMapNum(2); IMNFace f = polyObj.Mesh.F(i); polyNewFace.Mesh.F(0).Assign(f); IMNFace fnew = polyNewFace.Mesh.F(0); IList <int> vtx = f.Vtx; polyNewFace.Mesh.SetNumVerts(vtx.Count); for (int k = 0; k < vtx.Count; k++) { int nvindex = vtx[k]; IMNVert vert = polyObj.Mesh.V(nvindex); Debug.Print("\nVertex = " + k + ", " + nvindex); polyNewFace.Mesh.V(k).Assign(vert); fnew.Vtx[k] = k; } int nedge = nedge = polyNewFace.Mesh.SimpleNewEdge(0, 1); IMNEdge edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(1, 2); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(2, 3); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(3, 0); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); polyNewFace.Mesh.FillInMesh(); // make it update. polyNewFace.Mesh.InvalidateGeomCache(); if (addShell) { AddOsmShell(n.Handle, shell); } if (addEditMesh) { AddOsmEditMesh(n.Handle); } if (collapseNode) { ip.CollapseNode(n, true); } // update transform to match object being exploded. n.SetNodeTM(0, mat); n.ObjOffsetPos = ptOffsetPos; n.ObjOffsetRot = quatOffsetRot; n.ObjOffsetScale = scaleOffsetScale; n.ObjOffsetPos = ptOffsetPos; if (centerPivot) { n.CenterPivot(0, false); } } } catch (Exception ex) { Debug.Print(ex.Message); return(-1); } return(1); }
/// <summary> /// This is the routine to convert the input node to triangle faces. /// </summary> /// <param name="nodeHandle"> Input the node by handle. </param> /// <param name="convertToTri"> Input whether to convert to a tri object first. </param> /// <param name="addShell"> Input whether to add the shell modifier when finished converting to face. </param> /// <param name="shell"> Input the shell thickness amount. </param> /// <param name="addEditMesh"> Input whether to add the Edit Mesh modifier when finished converting to face. </param> /// <param name="collapseNode"> Input whether to collapse the node afterwards. </param> /// <param name="centerPivot"> Input whether to center the pivot on each new face. </param> /// <returns> Returns 1 if successful or -1 if not. </returns> static public int ConvertToTriangleFaces(uint nodeHandle, bool convertToTri = true, // C# now supports default parameters bool addShell = true, float shell = 0.1f, bool addEditMesh = true, bool collapseNode = true, bool centerPivot = true) { try { IGlobal global = Autodesk.Max.GlobalInterface.Instance; IInterface14 ip = global.COREInterface14; IINode node = ip.GetINodeByHandle(nodeHandle); // Get it's current object state. If a modifier has been applied, for example, // it is going to return the OS of the mesh in it's current form in the timeline. IObjectState os = node.ObjectRef.Eval(ip.Time); // Now grab the object itself. IObject objOriginal = os.Obj; // Let's make sure it is a TriObject, which is the typical kind of object with a mesh if (!objOriginal.IsSubClassOf(global.TriObjectClassID)) { // If it is NOT, see if we can convert it... if (convertToTri && objOriginal.CanConvertToType(global.TriObjectClassID) == 1) { objOriginal = objOriginal.ConvertToType(ip.Time, global.TriObjectClassID); } else { return(-1); } } // Now we should be safe to know it is a TriObject and we can cast it as such. // An exception will be thrown... ITriObject triOriginal = objOriginal as ITriObject; // Let's first setup a class ID for the type of objects are are creating. // New TriObject in this case to hold each face. IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.TRIOBJ_CLASS_ID, 0); IMatrix3 mat = node.GetNodeTM(0, null); IPoint3 ptOffsetPos = node.ObjOffsetPos; IQuat quatOffsetRot = node.ObjOffsetRot; IScaleValue scaleOffsetScale = node.ObjOffsetScale; // We can grab the faces as a List and iterate them in .NET API. IMesh mesh = triOriginal.Mesh; IList <IFace> faces = triOriginal.Mesh.Faces; int nNumFaces = faces.Count; if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressMaxNum = nNumFaces; } ADN_UserBreakCheck checkUserBreak = new ADN_UserBreakCheck(); int count = 0; foreach (IFace face in faces) { if (checkUserBreak.Check() == true) { return(-1); } if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressCurrNum = ++count; } // Create a new TriObject for each new face. object objectNewFace = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID); // Create a new node to hold it in the scene. IObject objNewFace = (IObject)objectNewFace; IINode n = global.COREInterface.CreateObjectNode(objNewFace); // Name it and ensure it is unique... string newname = "ADN-Sample-Face"; ip.MakeNameUnique(ref newname); n.Name = newname; // Based on what we created above, we can safely cast it to TriObject ITriObject triNewFace = objNewFace as ITriObject; // Setup the new TriObject with 1 face, and the vertex count from the original object's face we are processing triNewFace.Mesh.SetNumFaces(1, false, false); triNewFace.Mesh.SetNumVerts(face.V.Count(), false, false); // Finish setting up the face (always face '0' because there will only be one per object). triNewFace.Mesh.Faces[0].SetVerts(0, 1, 2); triNewFace.Mesh.Faces[0].SetEdgeVisFlags(EdgeVisibility.Vis, EdgeVisibility.Vis, EdgeVisibility.Vis); triNewFace.Mesh.Faces[0].SmGroup = 2; // Now, for each vertex, get the old face's points and store into new. for (int i = 0; i < face.V.Count(); i++) { //Get the vertex from the original object's face we are processing IPoint3 point = triOriginal.Mesh.GetVert((int)face.GetVert(i)); // Set the vertex point in the new face vertex triNewFace.Mesh.SetVert(i, point); } // make it draw. triNewFace.Mesh.InvalidateGeomCache(); if (addShell) { AddOsmShell(n.Handle, shell); } if (addEditMesh) { AddOsmEditMesh(n.Handle); } if (collapseNode) { ip.CollapseNode(n, true); } // update transform to match object being exploded. n.SetNodeTM(0, mat); n.ObjOffsetPos = ptOffsetPos; n.ObjOffsetRot = quatOffsetRot; n.ObjOffsetScale = scaleOffsetScale; n.ObjOffsetPos = ptOffsetPos; if (centerPivot) { n.CenterPivot(0, false); } } } catch (Exception) { return(-1); } return(1); }