gH]kDwP|trueSpace7.6[VS_O93Y*I Clinton Reese, RUR`d8pM*#ͺOI)PNG  IHDRXIDATxoEzvkqI;j( !T! $ n='.*$8 DOGQi{wf88M*QJ}j<4MJ)!}4$i8 0zm;DZmۢ(2ƒ$!vvv f0Uzhi' Cڿ,c!L,˔w@!̥w 7ᵪgkHF0 eYvw&?1~A P s+{zz 9?x7"r3( E@JR&P^@23py~$m|bU;D̩s@ಯ^|3;BzĔ7> Xk{pXIENDB`/tfѭ8Lwb"q48@J g'JNjn*pceZU@䑧rceZU@䑧r$_scriptData*Internal Script data [җJs@ %Ƹ4W}@O{4W}@O{$ Owner4Owner of the Encapsulator$(XFҦV(XFҦV"LE2Data$LE2 Data for node >g'JNjn*pLE2Data, 1#J@#QLJ@#QLLE2Data$LE2 Data for node >g'JNjn*pLE2Data, 3MT^RwL~YYTMT^RwL~YYTޢLE2Data$LE2 Data for node >g'JNjn*pLE2Data, 2 GFI_|k GFI_|kDMyFileNameD2|=Bynj2|=Bynj Control OutTransitionOutz@Hoz@Ho Control InTransitionIn 2|=Bynj2AEHM'>g'JNjn*pnBC`@0@2|=Bynj|?z@Hoۙ>?ceZU@䑧r[җJs@ NewCommand*// Doom3 md5mesh export // // march 10 2009 discovered change in weight behaviour in truespace7.6 // need to test for type of weight list // materials wrong as well // // august 1 2008 2 rules // no leaf joint on origin bone // delete all ik handles and locks before export manually // // july 29 2008 skeleton is good, uv is on the way and big build of weight got most ideas down // with luck may finish after one more day of work // got stuck on vertices xyz causing crash // solution get mesh at editable mesh level otherwise reading skinning result // // july 28 2008 tS7.6 released last week, time to do this for real // note: change import so "Joint" isn't added to joint name, "Bone" for bones is good though // // april 4 2008 get this simple 1 bone export working then do it for real // result will read into lightwave but not truespace // try 2 bones for export - that did it can now read it back into truespace // next read actual joints // // april 15 2006 save transformed vertices // IT WORKS! TODO: test import to doom3 // reactivate mtr texture file // need way to "save file" dialog // // april 14 2006 try build so can have fewer uv's than vertices and will also do multiple uv per vertices // // march 25 2006 almost works - strange file problem and mesh not right indicated by import // test is square object made of 2 triangles // runs w/crash and seems to read uv info ok // // feb 5 2006 // clinton reese // // md5mesh FILE FORMAT // // MD5 header // numJoints // numMeshes // joint - list of joints // name // parent index - -1 means no parent // xyz position - absolute position // quaternion xyz - get w by calcualtion // mesh // header? // numverts // vert // index // u,v // start index for weight // number of weights // numtris // tri // index // vert index ijk - triangle definition by vert indices // numweights // weight // index // joint index // value // xyz - offset position from joint // // The position of a vertex is found from the sum of the weighted offset from joints // the weights add to 1.0 // That's not true any one of the joints can be used to find the position of the vertex in world space // // NOTE: need parseInt(), parseFloat() in some cases to make sure saved as a number // any time you add 2 things they both must be numbers or they add as strings // // //// Execute // Called to execute the command function Execute(params) { //********************** //NOTE: // doom3 max 4 weights per vertex // must have "origin" // looks like 1 uv per vertex //********************** // TODO: put your action code here var myScale = 10.0; // 1 inch in doom3 = 0.1 meter in trueSpace //july 2008 //make sure selected object has skeleton and at least 1 mesh meshFound = false; skeletonFound = false; meshCount = 0; meshes = new Array(); scene = Space.CurrentScene(); firstSel = Node.FirstSelected();//full path to selection System.Trace(firstSel); childCount = Node.SubObjectCount(firstSel); for(i=0;i<childCount;i++) { curNode = Node.SubObject(firstSel,i);//short name of child System.Trace(curNode); if(curNode=="Skeleton") skeletonFound = true; else if(Node.ConExists(firstSel + "/" + curNode,"Mesh")) { meshFound = true; meshes[meshCount] = curNode; meshCount++; } } if(!meshFound) System.Alert("No mesh found inside selected node"); if(!skeletonFound) System.Alert("No skeleton found inside selected node"); System.Trace("mesh count= " + meshCount); //build joint list //declare arrays, use for loop with break set all clear flag for each time run for loop if all clear at end then done jointList = new Array(); jointList.path = new Array();//full parth to joints jointList.parentIndex = new Array();//parent index in this array of the parent jointList.numChildren = new Array();//number of children for this joint jointList.curChild = new Array();//the current child node that is being explored //quaternion storage jointList.qx = new Array(); jointList.qy = new Array(); jointList.qz = new Array(); jointList.qw = new Array(); // //get first true doom3 joint // //skeleton root(joint like) connects to dummy bone which connects to dummy joint and first doom3 joint skelPath = firstSel + "/Skeleton"; num = Node.LinkedOutputNodeCount(skelPath + "/Skeleton root","Joint");//skeleton root is like an invisible first joint for truespace dummyBone = Node.LinkedOutputNode(skelPath + "/Skeleton root","Joint",0);//full path name of dummy bone //System.Trace(name); //dummy bone can only have 2 joints num = Node.LinkedOutputNodeCount(dummyBone,"Bone"); if(num>2) { System.Alert("to many joint connections to dummy bone"); return; } startJoint=""; for(i=0;i<num;i++) { jointName = Node.LinkedOutputNode(dummyBone,"Bone",i);//full joint path name num2 = Node.LinkedOutputNodeCount(jointName,"Joint"); //dummy joint has no children //if joint has child bone then is the start of doom3 heirarchy if(num2>0) { startJoint = jointName; numchild = num2; } //System.Trace("joint" + i +" has " + num2); } System.Trace(startJoint); //save origin joint to the list jointList.path[0] = startJoint;//full path to joint jointList.parentIndex[0] = -1; jointList.numChildren[0] = numchild; jointList.curChild[0] = 0; currIndex = 0;//track current joint lastIndex = 0;//add new joints to list here //put while loop start here while(currIndex != -1) { //add current child to list //if not the last joint child of the current joint if(jointList.curChild[currIndex] < jointList.numChildren[currIndex]) { //number of bone children of current joint - always 1 or 0 //ik handle and stops removed before export //no leaf joints allowed on origin joint //if joint has child bone then is not a leaf //child bone of the current joint curBone = Node.LinkedOutputNode(jointList.path[currIndex],"Joint",0); //current child joint currentChildIndex = jointList.curChild[currIndex];//read index of current child joint curJoint = Node.LinkedOutputNode(curBone,"Bone",currentChildIndex); //check for limb boneInOut = Node.LinkedInputNodeCount(curJoint,"Bone") + Node.LinkedOutputNodeCount(curJoint,"Bone"); if(boneInOut==2) { //limb found curJoint = Node.LinkedOutputNode(curJoint,"Bone"); } //if pre leaf joint then do leaf stuff otherwise continue //qleafbone and qleafjoint are guranteed to exist because of "no leaf joints on origin bone rule" qleafbone = Node.LinkedOutputNode(curJoint,"Joint",0); qleafjoint = Node.LinkedOutputNode(qleafbone,"Bone",0); //check for limb boneInOut = Node.LinkedInputNodeCount(qleafjoint,"Bone") + Node.LinkedOutputNodeCount(qleafjoint,"Bone"); if(boneInOut==2) { //limb found qleafjoint = Node.LinkedOutputNode(qleafjoint,"Bone"); } //check see if last joint(grandchild of current joint) has a child bone, if not then is a leaf leaftest = Node.LinkedOutputNodeCount(qleafjoint,"Joint"); if(leaftest!=0) { //get num children(joints) - get child bone of child joint then num joints hooked to that childBoneOfChildJoint = Node.LinkedOutputNode(curJoint,"Joint",0); numchild = Node.LinkedOutputNodeCount(childBoneOfChildJoint,"Bone");//number of grandchildren throught this child joint } else { numchild = 0;//leaf has no children } //save to the list lastIndex++; jointList.path[lastIndex] = curJoint;//full path to joint jointList.parentIndex[lastIndex] = currIndex; jointList.numChildren[lastIndex] = numchild; jointList.curChild[lastIndex] = 0; currIndex = lastIndex; } //last child of current joint else { parentIndex = jointList.parentIndex[currIndex]; //update the parent's current child index to point to next child jointList.curChild[parentIndex]++; //set parent as current index currIndex = parentIndex; } }//end of loop all joints are in the list when currIndex = -1 numJoints = lastIndex + 1; for(j=0;j<=lastIndex;j++) { System.Trace(jointList.path[j] + " " + jointList.parentIndex[j]); } //file dialog only good for openning files // no good for creating new files so just type in text box till something better comes along MyFileName = params.conValue('MyFileName'); //MyFileName = shortName + ".md5mesh"; var forReading = 1, forWriting = 2, forAppending = 8; fs = new ActiveXObject("Scripting.FileSystemObject"); fs.CreateTextFile(MyFileName,true); f = fs.GetFile(MyFileName); var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0; connToFile = f.OpenAsTextStream( forWriting, TristateUseDefault); //Header connToFile.WriteLine("MD5Version 10"); connToFile.WriteLine("commandline"); connToFile.WriteBlankLines(1); connToFile.WriteLine("numJoints " + numJoints); connToFile.WriteLine("numMeshes " + meshCount); connToFile.WriteBlankLines(1); connToFile.WriteLine("joints {"); myMatrix = System.CreateDO('Math Package/Matrix Float Data'); jointMatrix = System.CreateDO('Math Package/Matrix Float Data'); tempMatrix = System.CreateDO('Math Package/Matrix Float Data'); for(jind=0;jind<numJoints;jind++) { shortname = Node.ShortName(jointList.path[jind]); parentInd = jointList.parentIndex[jind]; if(parentInd != -1) parentname = Node.ShortName(jointList.path[parentInd]); else { parentname = ""; if(shortname != "origin") System.Alert("Warning: first joint not named 'origin'"); } myMatrix = Node.Value(jointList.path[jind],"WldMatrix"); xval = myMatrix.GetTransX() * myScale; yval = myMatrix.GetTransY() * myScale; zval = myMatrix.GetTransZ() * myScale; //theString = '\t' + '"' + shortname + '"\t' + parentInd + '( 0 0 1 ) ( 0 0 0 )\t\t// ' + parentname; theString = '\t' + '"' + shortname + '"\t' + parentInd + ' ( '; theString = theString + xval.toFixed(10) + ' ' + yval.toFixed(10) + ' ' + zval.toFixed(10) + ' ) '; qx = 0; qy = 0; qz = 0; qw = 0; mattoquat(myMatrix); //save quat of joint, need vertices xyz in relation to bone xyz theString = theString + '( ' + qx + ' ' + qy + ' ' + qz + ' )\t\t// ' theString = theString + parentname; connToFile.WriteLine(theString); } connToFile.WriteLine("}"); connToFile.WriteBlankLines(1); dM = System.CreateDO("Space 3D Package/Mesh Data"); dV = System.CreateDO("Space 3D Package/Vertex Stream Data"); dF = System.CreateDO("Space 3D Package/Triangle Vertices Stream Data"); dT = System.CreateDO("Space 3D Package/UV Coordinate Stream Data"); UVTri = System.CreateDO("Space 3D Package/UV Triangle Stream Data"); UVTriWork = System.CreateDO("Space 3D Package/UV Triangle Stream Data"); bM = System.CreateDO("Common Data Package/Bitmap Data"); weightInd = new Array();//each entry corresponds to a vertex and is a list of indexes into doom3 skeleton list weightList = new Array();//store actual weights here skinstream = System.CreateDO('Space 3D Package/Skinning direct stream data');//IRdSkinWeightDirect skinstreamI = System.CreateDO('Space 3D Package/Skinning index stream Data');//indexed skinparam = System.CreateDO('Space 3D Package/Skinning parameters DO'); outstream = System.CreateDO('Space 3D Package/Skinning direct stream data'); myVector = System.CreateDO('Math Package/Point Data'); for(curmesh=0;curmesh<meshCount;curmesh++) { connToFile.WriteLine("mesh {"); connToFile.WriteLine("\t// meshes: " + meshes[curmesh]); meshPath = firstSel + "/" + meshes[curmesh]; shaderName = "dummy"; //search inside mesh for material node lookMatCnt = Node.SubObjectCount(meshPath); for(lm=0;lm<lookMatCnt;lm++) { curSub = meshPath + "/" + Node.SubObject(meshPath,lm); //System.Trace(curSub); if(Node.ConExists(curSub,"Material List")) { //v1.6 materials wrong //System.Trace("Material List found"); //curSubSub = curSub + "/Material List"; lookMatCnt2 = Node.SubObjectCount(curSub); for(lm2=0;lm2<lookMatCnt2;lm2++) { curSubSub = curSub + "/" + Node.SubObject(curSub, lm2); //System.Trace(curSubSub); if(Node.ConExists(curSubSub,"Material Chunk")) //if(!Node.ConExists(curSubSub,"Material List")) //if(curSubSub=="Material 0") { //System.Trace("Material Chunk found"); lookMatCnt3 = Node.SubObjectCount(curSubSub); for(lm3=0;lm3<lookMatCnt3;lm3++) { curSSSub = curSubSub + "/" + Node.SubObject(curSubSub, lm3); if(Node.ConExists(curSSSub,"Material")) //if(curSSSub!="Material Flavor Manager") { //System.Trace("Material found"); shaderName = Node.ShortName(curSSSub); } } } } //shaderName = Node.ShortName(curSub); //convert % to / //re = /%/g; //shaderName = shaderName.replace(re,"/"); //connToFile.WriteLine('\tshader "' + shaderName + '"'); } } //convert % to / re = /%/g; shaderName = shaderName.replace(re,"/"); connToFile.WriteLine('\tshader "' + shaderName + '"'); //connToFile.WriteLine('\tshader "dummy' + curmesh + '"'); connToFile.WriteBlankLines(1); //get mesh from editable shape to avoid crashing dM = Node.Value(meshPath + "/Editable shape", 'Mesh');//editable shape added to stop crash mesh is coming to outside via skinning node dV = dM.GetVertices(); dF = dM.GetTriangles(); dT = dM.GetCustStreamByName("UV Coordinate Stream Data"); UVTri = dM.GetTrianglesStreamByName("UV Triangle Stream Data"); numverts = dM.GetNumVertices(); for(i=0;i<numverts;i++) { weightList[i] = "";//initialize the lists weightInd[i] = ""; } //build weight info one bone at a time skinparam = Node.Value(meshPath + "/Skinning","Weights"); for(bind=0;bind<numJoints;bind++) { //read joint theJoint = jointList.path[bind]; //get bone from joint theBone = Node.LinkedOutputNode(theJoint,"Joint",0); //get skinning data //use skindata to populate the weightInd with indices(doom3 joint index) and weight values in weightList skinstream = skinparam.GetSkinningData(theBone); skinstreamI = skinparam.GetSkinningData(theBone); //determine if weight list is direct or indirect directF = 1; indirectF = 1; try { numb = skinstreamI.GetNumRecords(); } catch(e) { indirectF = 0; } try { numb = skinstream.GetNumSkinWeights(); } catch(ee) { directF = 0; } //if(skinstream) if(directF==1) { for(w=0;w<numverts;w++) { wt = skinstream.weight(w); if(wt != 0) { //store it //make "," delimited lists - later ignore the first comma in each list, 5 "," bad to many influences weightList[w] = weightList[w] + "," + wt; weightInd[w] = weightInd[w] + "," + bind; } } } if(indirectF==1) { for(w=0;w<numb;w++) { wt = skinstreamI.weight(w); if(wt != 0) { //store it //make "," delimited lists - later ignore the first comma in each list, 5 "," bad to many influences weightList[w] = weightList[w] + "," + wt; weightInd[w] = weightInd[w] + "," + bind; } } } } numtris = dM.GetNumFaces(); numUV = dT.GetNumUVCoords(); numUVTri = UVTri.GetNumUVTripleIndices(); //UVTriWork.SetNumUVTripleIndices(7000); UVTriWork.SetNumUVTripleIndices(48000); // read each triangle definition to get the vertex/uv indices // straight read results in repeating indexes so build list of unique values // uv is the basis and may have fewer unique point values than actual vertices // build will make sure that there is unique combos of uv and vertex so will work for case of 1 vertex multiple uv // // build xyz uv table // april 14 - xyz and uv pointer arrays xyzPoint = new Array(); uvPoint = new Array(); countPoint = 0; var i; for(uvTriI = 0; uvTriI < numUVTri; uvTriI++) { UVpointIndex = UVTri.i(uvTriI); XYZpointIndex = dF.i(uvTriI); matchFound = 0; if(uvTriI ==0) { xyzPoint[countPoint] = XYZpointIndex; uvPoint[countPoint] = UVpointIndex; UVTriWork.i(uvTriI) = 0; countPoint++; } else { for(i=0;i<countPoint;i++) { if(XYZpointIndex == xyzPoint[i] && UVpointIndex == uvPoint[i]) { matchFound = 1; UVTriWork.i(uvTriI) = i; } } if (matchFound == 0) { xyzPoint[countPoint] = XYZpointIndex; uvPoint[countPoint] = UVpointIndex; UVTriWork.i(uvTriI) = countPoint; countPoint++; } } UVpointIndex = UVTri.j(uvTriI); XYZpointIndex = dF.j(uvTriI); matchFound = 0; for(i=0;i<countPoint;i++) { if(XYZpointIndex == xyzPoint[i] && UVpointIndex == uvPoint[i]) { matchFound = 1; UVTriWork.j(uvTriI) = i; } } if (matchFound == 0) { xyzPoint[countPoint] = XYZpointIndex; uvPoint[countPoint] = UVpointIndex; UVTriWork.j(uvTriI) = countPoint; countPoint++; } UVpointIndex = UVTri.k(uvTriI); XYZpointIndex = dF.k(uvTriI); matchFound = 0; for(i=0;i<countPoint;i++) { if(XYZpointIndex == xyzPoint[i] && UVpointIndex == uvPoint[i]) { matchFound = 1; UVTriWork.k(uvTriI) = i; } } if (matchFound == 0) { xyzPoint[countPoint] = XYZpointIndex; uvPoint[countPoint] = UVpointIndex; UVTriWork.k(uvTriI) = countPoint; countPoint++; } } connToFile.WriteLine("\tnumverts " + countPoint); //note: looks like doom3 files recycle the weight so 2 uv/verts can point to same weight data // will leave expanded version should be ok // indicates that 1 point can have 2 uvs in doom3 file weightRunning = 0; for(xyzuvi = 0; xyzuvi< countPoint; xyzuvi++) { tu = dT.u(uvPoint[xyzuvi]); tv = dT.v(uvPoint[xyzuvi]); pi = xyzPoint[xyzuvi]; wi = weightInd[pi];//count this commas to get num weights and keep running count as well ss = wi.split(","); curwtcount = ss.length - 1;//subtract for leading comma //connToFile.WriteLine("\tvert " + xyzuvi + " ( " + tu.toFixed(10) + " " + tv.toFixed(10) + " ) " + xyzPoint[xyzuvi] + " " + curwtcount); connToFile.WriteLine("\tvert " + xyzuvi + " ( " + tu.toFixed(10) + " " + tv.toFixed(10) + " ) " + weightRunning + " " + curwtcount); weightRunning += curwtcount; } connToFile.WriteBlankLines(1); connToFile.WriteLine("\tnumtris " + numtris); for(fi = 0; fi< numtris; fi++) { connToFile.WriteLine("\ttri " + fi + " " + UVTriWork.k(fi) + " " + UVTriWork.j(fi) + " " + UVTriWork.i(fi) );//note kji order } connToFile.WriteBlankLines(1); connToFile.WriteLine("\tnumweights " + weightRunning); //build in same order as uv var weightV = 0; theWtInd = 0; for(vi = 0; vi< countPoint; vi++) { //point index pi = xyzPoint[vi]; //weight index wi = weightInd[pi];//count this commas to get num weights and keep running count as well wiArray = wi.split(","); //weights wl = weightList[pi]; wlArray = wl.split(","); curwtcount = wiArray.length;//note has prefix comma //start loop at 1 to skip first entry(comma prefix) for(wt=1;wt<curwtcount;wt++) { //process each weight for the vertex jind = wiArray[wt]; weightV = wlArray[wt]*1;//mult by 1 to convert to number //get point world coords myMatrix = Node.Value(meshPath,"WldMatrix"); myVector.x = dV.x(pi); myVector.y = dV.y(pi); myVector.z = dV.z(pi); myVector = myMatrix.TransformPoint(myVector); //get vector from joint to vertex myVector jointMatrix = Node.Value(jointList.path[jind],"WldMatrix"); jointX = jointMatrix.GetTransX(); jointY = jointMatrix.GetTransY(); jointZ = jointMatrix.GetTransZ(); myVector.x = myVector.x - jointX; myVector.y = myVector.y - jointY; myVector.z = myVector.z - jointZ; //apply inverse of joint matrix to the vector //remove translation from inverse calculation jointMatrix.SetTransX(0); jointMatrix.SetTransY(0); jointMatrix.SetTransZ(0); myMatrix = jointMatrix.Invert; myVector = myMatrix.TransformPoint(myVector); tx = myVector.x * myScale; ty = myVector.y * myScale; tz = myVector.z * myScale; connToFile.WriteLine("\tweight " + theWtInd + " " + jind + " " + weightV.toFixed(10) + " ( " + tx.toFixed(10) + " " + ty.toFixed(10) + " " + tz.toFixed(10) + " )"); //connToFile.WriteLine("\tweight " + theWtInd + " " + jind + " " + weightV.toFixed(10));//getting mysterious crash with line above don't know why theWtInd++; } } connToFile.WriteLine("}"); } connToFile.Close(); } function ExploreNode(thePath, fp) { //must declare or will get global scope var curIndex; var curnumObj; var childPath, MyObj; //if oBitmap and owner has color_shader then do _d theOwner = Node.Owner(thePath); if(Node.ConExists(thePath,'oBitmap')) { bM = Node.Value(thePath,'oBitmap'); bmpName = bM.GetFileName(); nameIndex = bmpName.lastIndexOf("\\") + 1; picName = bmpName.substr(nameIndex); //picName = "test"; if(Node.ConExists(theOwner,'Color_shader')) { System.Trace("diffuse found " + "models/TSdoom3/Textures/" +picName); fp.WriteLine("\tdiffusemap " + "models/TSdoom3/Textures/" + picName); } if(Node.ConExists(theOwner,'Normal_shader')) { System.Trace("normal found"); fp.WriteLine("\tbumpmap " + "models/TSdoom3/Textures/" + picName); //if new normal map //inbitmap.SaveToFile(0,"mytest.bmp"); } if(Node.ConExists(theOwner,'Model_shader')) { System.Trace("specular found"); fp.WriteLine("\tspecularmap " + "models/TSdoom3/Textures/" + picName); } } //get first child curIndex = 0; curnumObj = Node.SubObjectCount(thePath); if(curnumObj > 0) { MyObj = Node.SubObject(thePath, 0); childPath = thePath + '/' + MyObj; } //while can still get child do recursive call while(curnumObj > 0) { ExploreNode(childPath, fp); curIndex = curIndex + 1; if(curIndex < curnumObj) { MyObj = Node.SubObject(thePath, curIndex); childPath = thePath + '/' + MyObj; } else { curnumObj = 0; } } } //from Advanced Animation and rendering techniques Alan Watt, Mark Watt //math for tr > 0 verified //tr <= 0 take on faith function mattoquat(myMatrix) { qx = 1; qy = 2; qz = 3; qw = 4; nxt = new Array(); nxt[0] = 1; nxt[1] = 2; nxt[2] = 0;//YZX tr = myMatrix.GetAt(0,0) + myMatrix.GetAt(1,1) + myMatrix.GetAt(2,2); if(tr > 0) { s = Math.sqrt(tr + 1); qw = 0.5 * s; s = 0.5 / s; //qx = s * (myMatrix.GetAt(1,2) - myMatrix.GetAt(2,1)); //qy = s * (myMatrix.GetAt(2,0) - myMatrix.GetAt(0,2)); //qz = s * (myMatrix.GetAt(0,1) - myMatrix.GetAt(1,0)); //make negative for doom3 quats(had do same for lightwave exporter qx = -s * (myMatrix.GetAt(1,2) - myMatrix.GetAt(2,1)); qy = -s * (myMatrix.GetAt(2,0) - myMatrix.GetAt(0,2)); qz = -s * (myMatrix.GetAt(0,1) - myMatrix.GetAt(1,0)); } else { //TODO: put real code here, possible is not needed we'll see //found need when exporting animation so add here too //for doom3 get flipping so use w to determine sign of result later qx = 1; qy = 2; qz = 3; qw = 4; i=0;//X if(myMatrix.GetAt(1,1) > myMatrix.GetAt(0,0)) i = 1;//Y if(myMatrix.GetAt(2,2) > myMatrix.GetAt(i,i)) i = 2;//Z j = nxt[i]; k = nxt[j] s = Math.sqrt((myMatrix.GetAt(i,i) - (myMatrix.GetAt(j,j) + myMatrix.GetAt(k,k))) + 1); if(i==0) qx = -s * 0.5; if(i==1) qy = -s * 0.5; if(i==2) qz = -s * 0.5; s = 0.5 / s; qw = (myMatrix.GetAt(j,k) - myMatrix.GetAt(k,j)) * s; qj = (myMatrix.GetAt(i,j) + myMatrix.GetAt(j,i)) * s; if(j==0) qx = -qj; if(j==1) qy = -qj; if(j==2) qz = -qj; qk = (myMatrix.GetAt(i,k) + myMatrix.GetAt(k,i)) * s; if(k==0) qx = -qk; if(k==1) qy = -qk; if(k==2) qz = -qk; } return; } NScript Object Package/jScript languageVstringMyFileName GFI_|k4W}@O{ GFI_|kvC:\Documents and Settings\Clinton\Desktop\pendulum.md5mesh(XFҦV>g'JNjn*pn5Dw@ c@2|=Bynj|?z@Hoۙ>?J@#QL>g'JNjn*pnBB`@T@2|=Bynj|?z@Hoۙ>?-1OSM n 럆L<_ї˶  DefaultControl  GFʠN=PnK5 }}P} z@HoZ`Ez]UO1釥GP>aPnK,z@HoZ`Ez]UO GFʠN=F nK5 }}P}  GFI_|kZ`Ez]UO1釥GP>aH nK, GFI_|kZ`Ez]UO GFʠN= P0nK5 }}P} 2|=BynjZ`Ez]UO1釥GP>aP 0nK,2|=BynjZ`Ez]UO#nKSM20`PnK$\VV GFI_|kZ`Ez]UOOˁ CRih[ PӁ e8 8Activity.Run('%THIS_NAME%') StartZ`Ez]UOOˁ CRih[ 0PӁ e8 :Activity.Stop('%THIS_NAME%') StopZ`Ez]UO1釥GP>a0xPPӁ , GFI_|kZ`Ez]UO GFʠN=PxxPӁ }}P} Click "..." then choose a file or rt click to create a new text file with an "md5mesh" extension instead of "txt". Click Start to save the md5mesh.Z`Ez]UO#nKSM2 PӁ $\VV GFI_|kZ`Ez]UOMT^RwL~YYT>g'JNjn*pnCB`@0@2|=Bynj|?z@Hoۙ>?z@Ho$