public static List <BXDAMesh> ExportMeshes(RigidNode_Base baseNode, bool useOCL = false) { SurfaceExporter surfs = new SurfaceExporter(); BXDJSkeleton.SetupFileNames(baseNode, true); List <RigidNode_Base> nodes = new List <RigidNode_Base>(); baseNode.ListAllNodes(nodes); SynthesisGUI.Instance.ExporterSetMeshes(nodes.Count); List <BXDAMesh> meshes = new List <BXDAMesh>(); foreach (RigidNode_Base node in nodes) { SynthesisGUI.Instance.ExporterSetOverallText("Exporting " + node.ModelFileName); if (node is RigidNode && node.GetModel() != null && node.ModelFileName != null && node.GetModel() is CustomRigidGroup) { Console.WriteLine("Exporting " + node.ModelFileName); try { SynthesisGUI.Instance.ExporterReset(); CustomRigidGroup group = (CustomRigidGroup)node.GetModel(); surfs.Reset(node.GUID); Console.WriteLine("Exporting meshes..."); surfs.ExportAll(group, (long progress, long total) => { double totalProgress = (((double)progress / (double)total) * 100.0); SynthesisGUI.Instance.ExporterSetSubText(String.Format("Export {1} / {2}", Math.Round(totalProgress, 2), progress, total)); SynthesisGUI.Instance.ExporterSetProgress(totalProgress); }); Console.WriteLine(); BXDAMesh output = surfs.GetOutput(); Console.WriteLine("Output: " + output.meshes.Count + " meshes"); Console.WriteLine("Computing colliders..."); output.colliders.Clear(); output.colliders.AddRange(ConvexHullCalculator.GetHull(output)); meshes.Add(output); } catch (Exception e) { Console.WriteLine(e.ToString()); throw new Exception("Error exporting mesh: " + node.GetModelID()); } } SynthesisGUI.Instance.ExporterStepOverall(); } return(meshes); }
/// <summary> /// The lite equivalent of the 'Start Exporter' <see cref="Button"/> in the <see cref="ExporterForm"/>. Used in <see cref="ExporterWorker_DoWork(Object, "/> /// </summary> /// <seealso cref="ExporterWorker_DoWork"/> /// <param name="baseNode"></param> /// <returns></returns> public List <BXDAMesh> ExportMeshesLite(RigidNode_Base baseNode) { SurfaceExporter surfs = new SurfaceExporter(); BXDJSkeleton.SetupFileNames(baseNode, true); List <RigidNode_Base> nodes = new List <RigidNode_Base>(); baseNode.ListAllNodes(nodes); List <BXDAMesh> meshes = new List <BXDAMesh>(); foreach (RigidNode_Base node in nodes) { SetProgressText("Exporting " + node.ModelFileName); if (node is RigidNode && node.GetModel() != null && node.ModelFileName != null && node.GetModel() is CustomRigidGroup) { try { CustomRigidGroup group = (CustomRigidGroup)node.GetModel(); surfs.Reset(node.GUID); surfs.ExportAll(group, (long progress, long total) => { SetProgressText(String.Format("Export {0} / {1}", progress, total)); }); BXDAMesh output = surfs.GetOutput(); output.colliders.Clear(); output.colliders.AddRange(ConvexHullCalculator.GetHull(output)); meshes.Add(output); } catch (Exception e) { throw new Exception("Error exporting mesh: " + node.GetModelID(), e); } } } return(meshes); }
/// <summary> /// Executes the actual exporting. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void exporter_DoWork(object sender, DoWorkEventArgs e) { FieldDefinition fieldDefinition = FieldDefinition.Factory(Guid.NewGuid(), Program.ASSEMBLY_DOCUMENT.DisplayName); foreach (PropertySet ps in Program.MAINWINDOW.GetPropertySetsTabControl().TranslateToPropertySets()) { fieldDefinition.AddPropertySet(ps); } SurfaceExporter surfaceExporter = new SurfaceExporter(); List <string> exportedMeshes = new List <string>(); List <string> exportedColliders = new List <string>(); StringBuilder pathBuilder = new StringBuilder(); int numOccurrences = Program.ASSEMBLY_DOCUMENT.ComponentDefinition.Occurrences.AllLeafOccurrences.Count; int progressPercent = 0; int currentOccurrenceID = 0; foreach (ComponentOccurrence currentOccurrence in Program.ASSEMBLY_DOCUMENT.ComponentDefinition.Occurrences.AllLeafOccurrences) { if (exporter.CancellationPending) { e.Cancel = true; return; } progressPercent = (int)Math.Floor((currentOccurrenceID / (double)numOccurrences) * 100.0); exporter.ReportProgress(progressPercent, "Exporting... " + progressPercent + "%"); if (currentOccurrence.Visible && currentOccurrence.ReferencedDocumentDescriptor != null && currentOccurrence.ReferencedDocumentDescriptor.ReferencedDocumentType == DocumentTypeEnum.kPartDocumentObject && currentOccurrence.SurfaceBodies.Count > 0) { FieldNode outputNode = new FieldNode(currentOccurrence.Name); outputNode.Position = Utilities.ToBXDVector(currentOccurrence.Transformation.Translation); outputNode.Rotation = Utilities.QuaternionFromMatrix(currentOccurrence.Transformation); if (!exportedMeshes.Contains(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName)) { surfaceExporter.Reset(); surfaceExporter.Export(((PartDocument)currentOccurrence.ReferencedDocumentDescriptor.ReferencedDocument).ComponentDefinition, false, true); BXDAMesh.BXDASubMesh outputMesh = surfaceExporter.GetOutput().meshes.First(); exportedMeshes.Add(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); fieldDefinition.AddSubMesh(outputMesh); } outputNode.SubMeshID = exportedMeshes.IndexOf(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); ComponentPropertiesTabPage componentProperties = Program.MAINWINDOW.GetPropertySetsTabControl().GetParentTabPage(currentOccurrence.Name); if (componentProperties != null) { outputNode.PropertySetID = componentProperties.Name; PropertySet propertySet = fieldDefinition.GetPropertySets()[outputNode.PropertySetID]; if (propertySet.Collider.CollisionType == PropertySet.PropertySetCollider.PropertySetCollisionType.MESH && ((PropertySet.MeshCollider)propertySet.Collider).Convex) { if (!exportedColliders.Contains(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName)) { exportedColliders.Add(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); fieldDefinition.AddCollisionMesh(ConvexHullCalculator.GetHull(fieldDefinition.GetSubMesh(outputNode.SubMeshID))); } outputNode.CollisionMeshID = exportedColliders.IndexOf(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); } } pathBuilder.Clear(); foreach (ComponentOccurrence co in currentOccurrence.OccurrencePath) { pathBuilder.Append(co.Name + "/"); } pathBuilder.Length--; fieldDefinition.NodeGroup[pathBuilder.ToString()] = outputNode; } currentOccurrenceID++; } exporter.ReportProgress(100, "Export Successful!"); fieldDefinition.GetMeshOutput().WriteToFile(filePathTextBox.Text + "\\mesh.bxda"); BXDFProperties.WriteProperties(filePathTextBox.Text + "\\definition.bxdf", fieldDefinition); // Use the commented code below for debugging. /** / * string result; * FieldDefinition readDefinition = BXDFProperties.ReadProperties(filePathTextBox.Text + "\\definition.bxdf", out result); * MessageBox.Show(result); * /**/ }
/// <summary> /// Executes the actual exporting. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void exporter_DoWork(object sender, DoWorkEventArgs e) { string directory = FIELD_FOLDER + fieldNameTextBox.Text; // Create directory if it does not exist if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // Warn user of overwrite if it does exist else if (MessageBox.Show("A field with this name already exists. Continue?", "Overwrite Existing Field", MessageBoxButtons.OKCancel) == DialogResult.Cancel) { e.Cancel = true; return; } FieldDefinition fieldDefinition = FieldDefinition.Factory(Guid.NewGuid(), Program.ASSEMBLY_DOCUMENT.DisplayName); foreach (PropertySet ps in Program.MAINWINDOW.GetPropertySetsTabControl().TranslateToPropertySets()) { fieldDefinition.AddPropertySet(ps); } SurfaceExporter surfaceExporter = new SurfaceExporter(); List <string> exportedMeshes = new List <string>(); List <string> exportedColliders = new List <string>(); StringBuilder pathBuilder = new StringBuilder(); int numOccurrences = Program.ASSEMBLY_DOCUMENT.ComponentDefinition.Occurrences.AllLeafOccurrences.Count; int progressPercent = 0; int currentOccurrenceID = 0; foreach (ComponentOccurrence currentOccurrence in Program.ASSEMBLY_DOCUMENT.ComponentDefinition.Occurrences.AllLeafOccurrences) { if (exporter.CancellationPending) { e.Cancel = true; return; } progressPercent = (int)Math.Floor((currentOccurrenceID / (double)numOccurrences) * 100.0); exporter.ReportProgress(progressPercent, "Exporting... " + progressPercent + "%"); if (currentOccurrence.Visible && currentOccurrence.ReferencedDocumentDescriptor != null && currentOccurrence.ReferencedDocumentDescriptor.ReferencedDocumentType == DocumentTypeEnum.kPartDocumentObject && currentOccurrence.SurfaceBodies.Count > 0) { FieldNode outputNode = new FieldNode(currentOccurrence.Name); outputNode.Position = Utilities.ToBXDVector(currentOccurrence.Transformation.Translation); outputNode.Rotation = Utilities.QuaternionFromMatrix(currentOccurrence.Transformation); if (!exportedMeshes.Contains(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName)) { surfaceExporter.Reset(); surfaceExporter.Export(((PartDocument)currentOccurrence.ReferencedDocumentDescriptor.ReferencedDocument).ComponentDefinition, false, true); BXDAMesh.BXDASubMesh outputMesh = surfaceExporter.GetOutput().meshes.First(); exportedMeshes.Add(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); fieldDefinition.AddSubMesh(outputMesh); } outputNode.SubMeshID = exportedMeshes.IndexOf(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); ComponentPropertiesTabPage componentProperties = Program.MAINWINDOW.GetPropertySetsTabControl().GetParentTabPage(currentOccurrence.Name); if (componentProperties != null) { outputNode.PropertySetID = componentProperties.Name; PropertySet propertySet = fieldDefinition.GetPropertySets()[outputNode.PropertySetID]; if (propertySet.Collider.CollisionType == PropertySet.PropertySetCollider.PropertySetCollisionType.MESH && ((PropertySet.MeshCollider)propertySet.Collider).Convex) { if (!exportedColliders.Contains(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName)) { exportedColliders.Add(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); var test = fieldDefinition.GetSubMesh(outputNode.SubMeshID); fieldDefinition.AddCollisionMesh(ConvexHullCalculator.GetHull(fieldDefinition.GetSubMesh(outputNode.SubMeshID))); } outputNode.CollisionMeshID = exportedColliders.IndexOf(currentOccurrence.ReferencedDocumentDescriptor.FullDocumentName); } } pathBuilder.Clear(); foreach (ComponentOccurrence co in currentOccurrence.OccurrencePath) { pathBuilder.Append(co.Name + "/"); } pathBuilder.Length--; fieldDefinition.NodeGroup[pathBuilder.ToString()] = outputNode; } currentOccurrenceID++; } exporter.ReportProgress(100, "Export Successful!"); fieldDefinition.GetMeshOutput().WriteToFile(directory + "\\mesh.bxda"); // Field data such as spawnpoints and gamepieces ExportFieldData(directory); // Property sets BXDFProperties.WriteProperties(directory + "\\definition.bxdf", fieldDefinition); // Open the export directory when done if (openFolderCheckBox.Checked) { Process.Start("explorer.exe", "/select, " + directory); } }
} // BoundsIntersectsCloud // area selection from multiple clouds, returns list of collectedpoints struct (which contains indexes to cloud and the actual point) public List<CollectedPoint> ConvexHullSelectPoints(GameObject go, List<Vector3> area) { // build area bounds var areaBounds = new Bounds(); for (int i = 0, len = area.Count; i < len; i++) { areaBounds.Encapsulate(area[i]); } // check bounds //PointCloudTools.DrawBounds(areaBounds, 100); // build area hull var calc = new ConvexHullCalculator(); var verts = new List<Vector3>(); var tris = new List<int>(); var normals = new List<Vector3>(); var mf = go.GetComponent<MeshFilter>(); if (go == null) Debug.LogError("Missing MeshFilter from " + go.name, go); var mesh = new Mesh(); mf.sharedMesh = mesh; calc.GenerateHull(area, false, ref verts, ref tris, ref normals); mesh.Clear(); mesh.SetVertices(verts); mesh.SetTriangles(tris, 0); mesh.SetNormals(normals); var results = new List<CollectedPoint>(); // all clouds for (int cloudIndex = 0, length2 = clouds.Count; cloudIndex < length2; cloudIndex++) { // exit if outside whole cloud bounds if (clouds[cloudIndex].bounds.Intersects(areaBounds) == false) return null; // check all nodes from this cloud for (int nodeIndex = 0; nodeIndex < clouds[cloudIndex].nodes.Length; nodeIndex++) { // early exit if bounds doesnt hit this node? if (clouds[cloudIndex].nodes[nodeIndex].bounds.Intersects(areaBounds) == false) continue; // loop points for (int j = 0, l = clouds[cloudIndex].nodes[nodeIndex].points.Count; j < l; j++) { // check all points from that node int pointIndex = clouds[cloudIndex].nodes[nodeIndex].points[j]; // get actual point Vector3 p = viewers[clouds[cloudIndex].viewerIndex].points[pointIndex]; // check if inside hull if (IsPointInsideMesh(mesh, p)) { var temp = new CollectedPoint(); temp.cloudIndex = cloudIndex; temp.pointIndex = pointIndex; results.Add(temp); } } } } // for clouds return results; }
void Update() { //writeResult = false; if (chremote.nrCHremote == chlocal.nrCHlocal && chremote.nrCHremote != 0 && chlocal.nrCHlocal != 0 && chlocal.readyForIntersectionLocal && chremote.readyForIntersectionRemote && eval.localIsDemonstrator) { //child = chremote.nrCHremote - 1; Debug.Log("Initiate Intersection"); try { child = chremote.nrCHremote - 1; /*if () * { * * }*/ CSG A = CSG.fromMesh(localVolumesParent.transform.GetChild(child).GetComponent <MeshFilter>().mesh, localVolumesParent.transform.GetChild(child)); CSG B = CSG.fromMesh(remoteVolumesParent.transform.GetChild(child).GetComponent <MeshFilter>().mesh, remoteVolumesParent.transform.GetChild(child)); CSG result = null; result = A.intersect(B); Debug.Log(A.polygons.Count + ", " + B.polygons.Count + ", " + result.polygons.Count); GameObject newGo = Instantiate(meshLess, Vector3.zero, Quaternion.identity) as GameObject; newGo.transform.SetParent(IntParent.transform, false); intersectionMesh = result.toMesh(); newGo.GetComponent <MeshFilter>().mesh = intersectionMesh; float volumeCSG = (VolumeOfMesh(intersectionMesh) * 1000000); Debug.Log("Volume from CSG Algorithm: " + volumeCSG + ". %: " + volumeCSG / chlocal.volume * 100.0f); newGo.SetActive(false); //////////////////////////////////////////////////////////////////////////////////// //////// PASS TO CONEX HULL ///////////////////// //Parametros necessarios para o algoritmo de Convex Hull var calc = new ConvexHullCalculator(); var verts = new List <Vector3>(); var tris = new List <int>(); var normals = new List <Vector3>(); Debug.Log("Intersection points Hash:" + result.intersectionPoints.Count); for (int i = 0; i < result.intersectionPoints.Count - 1; i++) { float x = (float)Math.Ceiling(result.intersectionPoints.ToList()[i].x * 10000000) / 10000000; float y = (float)Math.Ceiling(result.intersectionPoints.ToList()[i].y * 10000000) / 10000000; float z = (float)Math.Ceiling(result.intersectionPoints.ToList()[i].z * 10000000) / 10000000; Vector3 position = new UnityEngine.Vector3(x, y, z); Instantiate(ball, position, Quaternion.identity); } if (result.intersectionPoints.Count >= 4) { calc.GenerateHull(result.intersectionPoints.ToList(), true, ref verts, ref tris, ref normals); //Create an initial transform that will evolve into our Convex Hull when altering the mesh var initialHull = Instantiate(initialMeshCSG); //initialHull = Instantiate(initialMesh); initialHull.transform.SetParent(IntParent.transform, false); initialHull.transform.position = Vector3.zero; initialHull.transform.rotation = Quaternion.identity; initialHull.transform.localScale = Vector3.one; //Independentemente do tipo de mesh com que se começa (cubo, esfera..) //a mesh é redefenida com as definiçoes abaixo var mesh = new Mesh(); mesh.SetVertices(verts); mesh.SetTriangles(tris, 0); mesh.SetNormals(normals); initialHull.GetComponent <MeshFilter>().sharedMesh = mesh; initialHull.GetComponent <MeshCollider>().sharedMesh = mesh; ///////////////////////////////////////////////////////////////////// //Calcular o volume da mesh volumeOfIntersection = VolumeOfMesh(mesh) * 1000000; //convert to cm3 //Compare the volume of intersection with the volume that the local pointed percentageOfIntersection = volumeOfIntersection / chlocal.volume * 100.0f; } else { volumeOfIntersection = 0.0f; percentageOfIntersection = 0.0f; } Debug.Log("Volume from CH Algorithm: " + volumeOfIntersection + ". %: " + percentageOfIntersection); //Debug.Log("Percentage of Intersection: " + percentageOfIntersection.ToString("F0") + "%"); percentageString = chlocal.volume.ToString() + '#' + chremote.volume.ToString() + '#' + volumeOfIntersection.ToString() + '#' + percentageOfIntersection.ToString("F1"); writeResult = true; Debug.Log(percentageString); initialMeshCSG.SetActive(false); localVolumesParent.transform.GetChild(child).gameObject.SetActive(false); remoteVolumesParent.transform.GetChild(child).gameObject.SetActive(false); chlocal.readyForIntersectionLocal = false; chremote.readyForIntersectionRemote = false; Debug.Log("Intersection write = " + writeResult); } catch (System.ArgumentException) { //newGo.SetActive(false); initialMeshCSG.SetActive(false); localVolumesParent.transform.GetChild(child).gameObject.SetActive(false); remoteVolumesParent.transform.GetChild(child).gameObject.SetActive(false); chlocal.readyForIntersectionLocal = false; chremote.readyForIntersectionRemote = false; /*foreach (Transform child in POINTS) * { * Destroy(child.gameObject); * }*/ startEnd.getStartTime = true; percentageString = "Error computing: Can't generate hull, points are coplanar!"; writeResult = true; } catch (UnityEngine.Assertions.AssertionException) { //newGo.SetActive(false); initialMeshCSG.SetActive(false); localVolumesParent.transform.GetChild(child).gameObject.SetActive(false); remoteVolumesParent.transform.GetChild(child).gameObject.SetActive(false); chlocal.readyForIntersectionLocal = false; chremote.readyForIntersectionRemote = false; /*foreach (Transform child in POINTS) * { * Destroy(child.gameObject); * }*/ startEnd.getStartTime = true; percentageString = "Error computing: Assertion failed!"; writeResult = true; } //throw new System.ArgumentException("Can't generate hull, points are coplanar"); //throw new UnityEngine.Assertions.AssertionException("Assertion failed", ""); } /*else if (chremote.nrCHremote == chlocal.nrCHlocal && chremote.nrCHremote != 0 && chlocal.nrCHlocal != 0 && eval.localIsDemonstrator && (!chlocal.readyForIntersectionLocal || !chremote.readyForIntersectionRemote) ) * { * percentageString = "Error computing initial hulls: Assertion failed!"; * //writeResult = true; * * Debug.Log("Stuck here"); * }*/ }
//public bool readyRemote = true; //public bool readylocal = true; //public IntersectionCSGTry intersectionCSG; void Start() { //Intersection through CSG //return; CSG A = CSG.fromMesh(a.GetComponent <MeshFilter>().mesh, a.transform); CSG B = CSG.fromMesh(b.GetComponent <MeshFilter>().mesh, b.transform); //CSG A = CSG.fromMesh(a.GetComponent<MeshFilter>().mesh, a.transform.GetChild(0)); //CSG B = CSG.fromMesh(b.GetComponent<MeshFilter>().mesh, b.transform.GetChild(0)); Debug.Log("Antes Interseção"); CSG result = A.intersect(B); //Debug.Log("Depois Interseção"); intersectionPoints = CSGtoListOfPoints(result.polygons); //Debug.Log("Depois dos pontos: " + intersectionPoints.Count + "pontos"); //Parametros necessarios para o algoritmo de Convex Hull var calc = new ConvexHullCalculator(); var verts = new List <Vector3>(); var tris = new List <int>(); var normals = new List <Vector3>(); //Debug.Log("Antes da CH" ); for (int pi = 0; pi < intersectionPoints.Count; pi++) { Instantiate(ball, intersectionPoints[pi], Quaternion.identity, POINTS); //ballsList.Add((Instantiate(ball, intersectionPoints[pi], Quaternion.identity)).position); //Debug.Log("Tau"); } for (int pi = 0; pi < POINTS.childCount; pi++) { ballsList.Add(POINTS.GetChild(pi).position); } calc.GenerateHull(ballsList, true, ref verts, ref tris, ref normals); //Debug.Log("Depois da CH"); //Create an initial transform that will evolve into our Convex Hull when altering the mesh var initialHull = Instantiate(initialMeshCSG); initialHull.transform.SetParent(transform, false); initialHull.transform.position = Vector3.zero; initialHull.transform.rotation = Quaternion.identity; initialHull.transform.localScale = Vector3.one; //Independentemente do tipo de mesh com que se começa (cubo, esfera..) //a mesh é redefenida com as definiçoes abaixo var mesh = new Mesh(); mesh.SetVertices(verts); mesh.SetTriangles(tris, 0); mesh.SetNormals(normals); initialHull.GetComponent <MeshFilter>().sharedMesh = mesh; initialHull.GetComponent <MeshCollider>().sharedMesh = mesh; //Calcular o volume da CH volume = VolumeOfMesh(mesh) * 1000000; //convert to cm3 Debug.Log("Volume: " + volume); //Limpar os pontos antigos da lista para o proximo convex hull e //informar o programa de que já realizou esta função //intersectionCSG.intersectionPoints.Clear(); initialMeshCSG.SetActive(false); a.SetActive(false); b.SetActive(false); }
/// <summary> /// The lite equivalent of the 'Start Exporter' <see cref="Button"/> in the <see cref="ExporterForm"/>. Used in <see cref="ExporterWorker_DoWork(Object, "/> /// </summary> /// <seealso cref="ExporterWorker_DoWork"/> /// <param name="baseNode"></param> /// <returns></returns> public List <BXDAMesh> ExportMeshesLite(RigidNode_Base baseNode, float totalMassKg) { SurfaceExporter surfs = new SurfaceExporter(); BXDJSkeleton.SetupFileNames(baseNode); List <RigidNode_Base> nodes = new List <RigidNode_Base>(); baseNode.ListAllNodes(nodes); List <BXDAMesh> meshes = new List <BXDAMesh>(); SetProgress(0, "Exporting Model"); for (int i = 0; i < nodes.Count; i++) { RigidNode_Base node = nodes[i]; if (node is RigidNode && node.GetModel() != null && node.ModelFileName != null && node.GetModel() is CustomRigidGroup) { try { CustomRigidGroup group = (CustomRigidGroup)node.GetModel(); BXDAMesh output = surfs.ExportAll(group, node.GUID, (long progress, long total) => { SetProgress(((double)progress / total) / nodes.Count + (double)i / nodes.Count); }); output.colliders.Clear(); output.colliders.AddRange(ConvexHullCalculator.GetHull(output)); meshes.Add(output); } catch (Exception e) { throw new Exception("Error exporting mesh: " + node.GetModelID(), e); } } } // Apply custom mass to mesh if (totalMassKg > 0) // Negative value indicates that default mass should be left alone (TODO: Make default mass more accurate) { float totalDefaultMass = 0; foreach (BXDAMesh mesh in meshes) { totalDefaultMass += mesh.physics.mass; } for (int i = 0; i < meshes.Count; i++) { meshes[i].physics.mass = totalMassKg * (float)(meshes[i].physics.mass / totalDefaultMass); } } // Add meshes to all nodes for (int i = 0; i < meshes.Count; i++) { ((OGL_RigidNode)nodes[i]).loadMeshes(meshes[i]); } // Get wheel information (radius, center, etc.) for all wheels foreach (RigidNode_Base node in nodes) { SkeletalJoint_Base joint = node.GetSkeletalJoint(); // Joint will be null if the node has no connection. // cDriver will be null if there is no driver connected to the joint. if (joint != null && joint.cDriver != null) { WheelDriverMeta wheelDriver = (WheelDriverMeta)joint.cDriver.GetInfo(typeof(WheelDriverMeta)); // Drivers without wheel metadata do not need radius, center, or width info. if (wheelDriver != null) { (node as OGLViewer.OGL_RigidNode).GetWheelInfo(out float radius, out float width, out BXDVector3 center); wheelDriver.radius = radius; wheelDriver.center = center; wheelDriver.width = width; joint.cDriver.AddInfo(wheelDriver); } } } return(meshes); }