public void OnImportButtonClicked() { // Debug.Log("ImportButton clicked"); if (!ObjIsTris()) { ShowNotTrisWarning(); return; } if (_activeObjElement != null) // make sure an obj is selected { string craftFile; string oldCraftId = (Game.Instance.Designer as DesignerScript).CraftScript.Data.Name; string newCraftId; if (!oldCraftId.Equals("New")) // if craft is saved { if (_newCraft) // make new file, or overwrite current one { newCraftId = oldCraftId + "_" + Path.GetFileNameWithoutExtension(_objName); (Game.Instance.Designer as DesignerScript).CraftScript.Data.Name = newCraftId; } else { newCraftId = oldCraftId; } Game.Instance.Designer.SaveCraft(newCraftId, newCraftId, false); craftFile = Game.Instance.CraftDesigns.RootFolderPath + newCraftId + ".xml"; } else // if craft is not saved. Basically always make new craft { newCraftId = oldCraftId + "_" + Path.GetFileNameWithoutExtension(_objName); (Game.Instance.Designer as DesignerScript).CraftScript.Data.Name = newCraftId; Game.Instance.Designer.SaveCraft(newCraftId, newCraftId, false); craftFile = Game.Instance.CraftDesigns.RootFolderPath + newCraftId + ".xml"; } string objFile = objDir + _activeObjElement.id; var options = new MeshOptions(craftFile, objFile, _shellWidth, _scale, _hasMass, _hasDrag, _hasCollision, _fuelTank); Debug.Log("Calling BuildMesh() with the following options:\n" + options); //Debug.Log(options); MeshBuilder.BuildMesh(options); Close(); } else { Game.Instance.Designer.ShowMessage("No file selected. Select an .obj file and try again"); } }
public static void BuildMesh(MeshOptions options) { Game.Instance.Designer.ShowMessage("Importing Model..."); string objName = Path.GetFileNameWithoutExtension(options.objFile); craftDoc = XDocument.Load(options.craftFile); XElement partsElement = craftDoc.Descendants().First(x => x.Name == "Parts"); XElement connectionsElement = craftDoc.Descendants().First(x => x.Name == "Connections"); XElement bodiesElement = craftDoc.Descendants().First(x => x.Name == "Bodies"); int lastPartId = Int32.Parse(partsElement.Elements().Last().Attribute("id").Value) + 1; int partIdOffset = 0; int meshBodyId = Int32.Parse(bodiesElement.Elements().Last().Attribute("id").Value) + 1; try { obj.LoadObj(options.objFile); } catch (Exception e) { Debug.LogError("Invalid .obj file: " + options.objFile); throw new Exception(e.Message); } var verts = obj.VertexList; var faces = obj.FaceList; if (Abs(options.scale - 1) > .0001) { verts = ScaleObj(verts, options.scale); } // root part partsElement.Add(new XElement("Part", new XAttribute("id", lastPartId), new XAttribute("partType", "Fuselage1"), new XAttribute("position", "0,0,0"), new XAttribute("rotation", "0,0,0"), new XAttribute("name", objName + "_root"), new XAttribute("commandPodId", "0"), new XAttribute("materials", "0,0,0,0,4"), new XElement("Drag", new XAttribute("drag", "0,0,0,0,0,0"), new XAttribute("area", "0,0,0,0,0,0")), new XElement("Config", new XAttribute("includeInDrag", options.hasDrag ? "true" : "false"), new XAttribute("partCollisionHandling", options.hasCollision ? "Default" : "Never"), new XAttribute("massScale", options.hasMass ? "1" : "0"), new XAttribute("heatShield", options.heatShield), new XAttribute("maxTemperature", options.maxTemp)), new XElement("Fuselage", new XAttribute("autoResize", "false"), new XAttribute("bottomScale", "0.05,0.05"), new XAttribute("offset", "0,0.05,0"), new XAttribute("topScale", "0.05,0.05")))); partIdOffset++; // handle part partsElement.Add(new XElement("Part", new XAttribute("id", lastPartId + partIdOffset), new XAttribute("partType", "Fuselage1"), new XAttribute("position", "0,0,0"), new XAttribute("rotation", "0,0,0"), new XAttribute("name", objName + "_handle"), new XAttribute("commandPodId", "0"), new XAttribute("materials", "0,0,0,0,4"), new XElement("Drag", new XAttribute("drag", "0,0,0,0,0,0"), new XAttribute("area", "0,0,0,0,0,0")), new XElement("Config", new XAttribute("includeInDrag", options.hasDrag ? "true" : "false"), new XAttribute("partCollisionHandling", options.hasCollision ? "Default" : "Never"), new XAttribute("massScale", options.hasMass ? "1" : "0"), new XAttribute("heatShield", options.heatShield), new XAttribute("maxTemperature", options.maxTemp)), new XElement("Fuselage", new XAttribute("autoResize", "false"), new XAttribute("bottomScale", "0.1,0.1"), new XAttribute("offset", "0,0.1,0"), new XAttribute("topScale", "0.1,0.1")))); connectionsElement.Add(new XElement("Connection", new XAttribute("partA", lastPartId + partIdOffset), new XAttribute("partB", lastPartId), new XAttribute("attachPointsA", "1"), new XAttribute("attachPointsB", "1"))); //adding Body element here, face parts will be added to this instead of making their own bodiesElement.Add(new XElement("Body", new XAttribute("id", meshBodyId), new XAttribute("partIds", lastPartId), new XAttribute("mass", "1"), new XAttribute("position", "0,0,0"), new XAttribute("rotation", "0,0,0"), new XAttribute("centerOfMass", "0,0,0"))); var bodyPartIds = bodiesElement.Elements().First(x => x.Attribute("id").Value == meshBodyId.ToString()) .Attribute("partIds"); Vector3 UnitX = new Vector3(1, 0, 0); Vector3 UnitY = new Vector3(0, 1, 0); Vector3 UnitZ = new Vector3(0, 0, 1); if (!options.fuelTank) // build using structural panels { options.shellWidth *= 10; // because structural panels are 0.1 wide (1/10th of scale) foreach (var face in faces) { partIdOffset++; var vert0 = GetFaceVertPos(face, verts, 0); var vert1 = GetFaceVertPos(face, verts, 1); var vert2 = GetFaceVertPos(face, verts, 2); //get values for shaping part to face var basePos = (vert0 + vert1) / 2; var partPos = (basePos + vert2) / 2; //not used for structural panels, their position is defined from the base var offset = vert2 - basePos; var baseWidth = (vert0 - vert1).magnitude; //get orientation and axes var vForward = (vert1 - vert0).normalized; //local Z axis var vRight = Vector3.Cross(vForward, offset).normalized; //local X axis var vUp = Vector3.Cross(vRight, vForward).normalized; //local Y axis //offset to account for the width of the structural panel float zFightOffset = (float)(1.0 / 5000); basePos += (float)(options.shellWidth * .05 - zFightOffset) * vRight; // *.05 is bc panels are .1 wide, and being offset by half Vector3 finalOffset = new Vector3(); finalOffset.x = ScalarProjection(offset, vRight); finalOffset.y = ScalarProjection(offset, vUp); finalOffset.z = ScalarProjection(offset, vForward); finalOffset *= (float).5; //because game values are half of the vector baseWidth *= (float).5; // using unity's quaternion to get euler angles from forward and up vectors Quaternion quat = new Quaternion(); quat.SetLookRotation((vForward), (vUp)); Vector3 eulerRot = new Vector3(quat.eulerAngles.x, quat.eulerAngles.y, quat.eulerAngles.z); //math done, adding to XML partsElement.Add(new XElement("Part", new XAttribute("id", lastPartId + partIdOffset), new XAttribute("partType", "StructuralPanel1"), new XAttribute("position", basePos.x + "," + basePos.y + "," + basePos.z), new XAttribute("rotation", eulerRot.x + "," + eulerRot.y + "," + eulerRot.z), new XAttribute("name", objName + "_" + partIdOffset), new XAttribute("commandPodId", "0"), new XAttribute("materials", "0,1,2,3,4"), new XElement("Drag", new XAttribute("drag", "0,0,0,0,0,0"), new XAttribute("area", "0,0,0,0,0,0")), new XElement("Config", new XAttribute("partScale", options.shellWidth + "," + options.shellWidth + "," + options.shellWidth), new XAttribute("massScale", options.shellWidth * (options.hasMass ? 1 : 0)), new XAttribute("dragScale", options.shellWidth * (options.hasDrag ? 1 : 0)), new XAttribute("includeInDrag", options.hasDrag ? "true" : "false"), new XAttribute("partCollisionHandling", options.hasCollision ? "Default" : "Never"), new XAttribute("heatShield", options.heatShield), new XAttribute("maxTemperature", options.maxTemp)), new XElement("Wing", new XAttribute("hingeDistanceFromTrailingEdge", "0.5"), new XAttribute("rootLeadingOffset", baseWidth * (1 / options.shellWidth)), new XAttribute("rootTrailingOffset", baseWidth * (1 / options.shellWidth)), new XAttribute("tipLeadingOffset", "0"), new XAttribute("tipPosition", (2 * finalOffset.x * (1 / options.shellWidth)) + "," + (2 * finalOffset.y * (1 / options.shellWidth)) + "," + (2 * finalOffset.z * (1 / options.shellWidth))), new XAttribute("tipTrailingOffset", "0")))); connectionsElement.Add(new XElement("Connection", new XAttribute("partA", lastPartId + partIdOffset), new XAttribute("partB", lastPartId), new XAttribute("attachPointsA", "1"), new XAttribute("attachPointsB", "0"))); bodyPartIds.Value += "," + (lastPartId + partIdOffset); } craftDoc.Save(options.craftFile); var loader = (Game.Instance.Designer as DesignerScript).CraftLoader; loader.LoadCraftInteractive(Path.GetFileNameWithoutExtension(options.craftFile), true, true, "'" + Path.GetFileName(options.objFile) + "'" + " Imported Successfully", null, null); // Game.Instance.Designer.ShowMessage("'" + Path.GetFileName(options.objFile) + "'" + " Imported Successfully"); obj = new Obj(); } else // build using fuel tanks { options.shellWidth *= 0.5; foreach (var face in faces) { partIdOffset++; var vert0 = GetFaceVertPos(face, verts, 0); var vert1 = GetFaceVertPos(face, verts, 1); var vert2 = GetFaceVertPos(face, verts, 2); //get values for shaping part to face var basePos = (vert0 + vert1) / 2; var partPos = (basePos + vert2) / 2; var offset = vert2 - basePos; var baseWidth = (vert0 - vert1).magnitude; //get orientation and axes var vForward = (vert1 - vert0).normalized; //local Z axis var vRight = Vector3.Cross(vForward, offset).normalized; //local X axis var vUp = Vector3.Cross(vRight, vForward).normalized; //local Y axis //offset to account for the width of the part float zFightOffset = (float)(1.0 / 5000); partPos += (float)(options.shellWidth - zFightOffset) * vRight; Vector3 finalOffset = new Vector3(); finalOffset.x = ScalarProjection(offset, vRight); finalOffset.y = ScalarProjection(offset, vUp); finalOffset.z = ScalarProjection(offset, vForward); // fuselages with 0 length break things if (finalOffset.y < 0.0001) { //Debug.Log("0 length fuselage skipped: " + partIdOffset + "\n" + // "offset: " + offsetFinalRot); partIdOffset--; continue; } // broken offset breaks things if (Double.IsInfinity(finalOffset.x) || Double.IsNaN(finalOffset.x) || Double.IsInfinity(finalOffset.y) || Double.IsNaN(finalOffset.y) || Double.IsInfinity(finalOffset.z) || Double.IsNaN(finalOffset.z)) { partIdOffset--; continue; } finalOffset *= (float).5; //because game values are half of the vector baseWidth *= (float).5; // using unity's quaternion to get euler angles from forward and up vectors Quaternion quat = new Quaternion(); quat.SetLookRotation((vForward), (vUp)); Vector3 eulerRot = new Vector3(quat.eulerAngles.x, quat.eulerAngles.y, quat.eulerAngles.z); //math done, adding to XML partsElement.Add(new XElement("Part", new XAttribute("id", lastPartId + partIdOffset), new XAttribute("partType", "Fuselage1"), new XAttribute("position", partPos.x + "," + partPos.y + "," + partPos.z), new XAttribute("rotation", eulerRot.x + "," + eulerRot.y + "," + eulerRot.z), new XAttribute("name", objName + "_" + partIdOffset), new XAttribute("commandPodId", "0"), new XAttribute("materials", "0,1,2,3,4"), new XAttribute("texture", "Default"), new XElement("Drag", new XAttribute("drag", "0,0,0,0,0,0"), new XAttribute("area", "0,0,0,0,0,0")), new XElement("AttachPoints", new XElement("AttachPoint", new XAttribute("id", "0"), new XAttribute("enabled", "false")), new XElement("AttachPoint", new XAttribute("id", "2"), new XAttribute("enabled", "false")), new XElement("AttachPoint", new XAttribute("id", "3"), new XAttribute("enabled", "false")), new XElement("AttachPoint", new XAttribute("id", "4"), new XAttribute("enabled", "false")), new XElement("AttachPoint", new XAttribute("id", "5"), new XAttribute("enabled", "false"))), new XElement("Config", new XAttribute("includeInDrag", options.hasDrag ? "true" : "false"), new XAttribute("partCollisionHandling", options.hasCollision ? "Default" : "Never"), new XAttribute("massScale", options.hasMass ? "1" : "0"), new XAttribute("heatShield", options.heatShield), new XAttribute("maxTemperature", options.maxTemp)), new XElement("Fuselage", new XAttribute("autoResize", "false"), new XAttribute("bottomScale", options.shellWidth + "," + baseWidth), new XAttribute("cornerRadiuses", "0,0,0,0,0,0,0,0"), new XAttribute("offset", finalOffset.x + "," + finalOffset.y + "," + finalOffset.z), new XAttribute("topScale", options.shellWidth + ",0")) )); connectionsElement.Add(new XElement("Connection", new XAttribute("partA", lastPartId + partIdOffset), new XAttribute("partB", lastPartId), new XAttribute("attachPointsA", "1"), new XAttribute("attachPointsB", "1"))); bodyPartIds.Value += "," + (lastPartId + partIdOffset); } craftDoc.Save(options.craftFile); Game.Instance.Designer.ShowMessage("Loading..."); var loader = (Game.Instance.Designer as DesignerScript).CraftLoader; loader.LoadCraftInteractive(Path.GetFileNameWithoutExtension(options.craftFile), true, true, "'" + Path.GetFileName(options.objFile) + "'" + " Imported Successfully", null, null); obj = new Obj(); craftDoc = null; } }