public static void FindModelFile() { var path = EditorUtility.OpenFilePanelWithFilters("Select model file", "Packages/com.unity.lego.modelimporter/Models", new string[] { "All model files", "ldr,io,lxfml,lxf", "LDraw files", "ldr", "Studio files", "io", "LXFML files", "lxfml", "LXF files", "lxf" }); if (path.Length != 0) { lxfml = ReadFileLogic(path); if (lxfml != null) { pivot = Model.Pivot.BottomCenter; filePath = path; importSettings.Clear(); foreach (var group in lxfml.groups) { importSettings.Add(group.number, new ModelGroupImportSettings()); } model = null; group = null; GetWindow <ImportModel>(true, "LEGO Model Importer"); } else { ShowReadError(); } } }
public static void ReimportModelGroup(ModelGroup group) { lxfml = ReadFileLogic(group.relativeFilePath); if (lxfml == null) { lxfml = ReadFileLogic(group.absoluteFilePath); } if (lxfml != null) { filePath = null; importSettings.Clear(); importSettings.Add(group.number, group.importSettings); // Check if group matches up with the file. // FIXME Next version could include option to match groups up manually. if (group.number >= lxfml.groups.Length || lxfml.groups[group.number].name != group.groupName) { EditorUtility.DisplayDialog("Reimport failed", $"Model group {group.number} {group.groupName} does not match up with group in file", "Ok"); return; } model = null; ImportModel.group = group; GetWindow <ImportModel>(true, "LEGO Model Importer"); } else { ShowReadError(); } }
private static LXFMLDoc.BrickGroup FindGroup(LXFMLDoc lxfml, LXFMLDoc.Brick brick) { foreach (var group in lxfml.groups) { if (group.brickRefs.Contains(brick.refId)) { return(group); } } return(null); }
private static void CreateDefaultGroup(LXFMLDoc lxfml) { lxfml.groups = new LXFMLDoc.BrickGroup[] { new LXFMLDoc.BrickGroup() }; var group = lxfml.groups[0]; group.name = "Default"; group.number = 0; group.brickRefs = new int[lxfml.bricks.Count]; for (var i = 0; i < lxfml.bricks.Count; ++i) { group.brickRefs[i] = lxfml.bricks[i].refId; group.bricks.Add(lxfml.bricks[i]); } }
private static bool FixLooseBricks(LXFMLDoc lxfml) { var looseBricks = new List <LXFMLDoc.Brick>(); foreach (var brick in lxfml.bricks) { if (!FindBrickInGroups(brick, lxfml.groups)) { looseBricks.Add(brick); } } if (looseBricks.Count > 0) { var newGroups = new List <LXFMLDoc.BrickGroup>(lxfml.groups); newGroups.Insert(0, new LXFMLDoc.BrickGroup()); lxfml.groups = newGroups.ToArray(); var group = newGroups[0]; group.name = "Main model"; group.number = 0; for (var i = 1; i < newGroups.Count; ++i) { newGroups[i].number = i; } group.brickRefs = new int[looseBricks.Count]; for (var i = 0; i < looseBricks.Count; ++i) { group.brickRefs[i] = looseBricks[i].refId; group.bricks.Add(looseBricks[i]); } return(true); } return(false); }
/// <summary> /// /// </summary> /// <param name="lxfmlDoc"></param> /// <param name="lxfml"></param> /// <returns></returns> public static bool ReadLxfml(XmlDocument lxfmlDoc, ref LXFMLDoc lxfml) { var success = false; if (lxfmlDoc != null) { try { success = true; var lxfmlNode = lxfmlDoc.SelectSingleNode("LXFML"); var cameraNode = lxfmlNode.SelectSingleNode("Cameras/Camera"); if (cameraNode != null) { var lxfmlCamera = new LXFMLDoc.LxfmlCamera(); lxfmlCamera.fov = float.Parse(cameraNode.Attributes["fieldOfView"].Value, CultureInfo.InvariantCulture); lxfmlCamera.distance = float.Parse(cameraNode.Attributes["distance"].Value, CultureInfo.InvariantCulture); lxfmlCamera.transformation = ParseUtils.StringToFloatArray(cameraNode.Attributes["transformation"].Value); var transformation = new Matrix4x4(); var mArr = ParseUtils.StringToFloatArray(cameraNode.Attributes["transformation"].Value); for (var i = 0; i < 4; ++i) { transformation.SetRow(i, new Vector4(mArr[i * 3], mArr[i * 3 + 1], mArr[i * 3 + 2], 0)); } lxfmlCamera.position = GetPosition(transformation); lxfmlCamera.rotation = GetRotation(transformation); lxfml.camera = lxfmlCamera; } var lxfmlNameAttrib = lxfmlNode.Attributes["name"]; if (lxfmlNameAttrib != null) { lxfml.name = lxfmlNameAttrib.Value; } var bricksNode = lxfmlNode.SelectSingleNode("Bricks"); var brickNodes = bricksNode.SelectNodes("Brick"); foreach (XmlNode brickNode in brickNodes) { var brick = PopulateBrick(brickNode); lxfml.bricks.Add(brick); } var groupsNode = lxfmlNode.SelectSingleNode("GroupSystems"); if (groupsNode != null) { var brickGroupSystemNode = groupsNode.SelectSingleNode("BrickGroupSystem"); // TODO: Handle PartGroupSystem. if (brickGroupSystemNode != null) { var rootGroupNodes = brickGroupSystemNode.SelectNodes("Group"); if (rootGroupNodes.Count > 0) { lxfml.groups = new LXFMLDoc.BrickGroup[rootGroupNodes.Count]; var groupCount = 0; foreach (XmlNode rootGroupNode in rootGroupNodes) { var group = new LXFMLDoc.BrickGroup(); var nameAttribute = rootGroupNode.Attributes["name"]; if (nameAttribute != null) { group.name = nameAttribute.Value; } group.number = groupCount; lxfml.groups[groupCount++] = group; //Sub groups! if (rootGroupNode.SelectNodes("Group").Count > 0) { ReadGroupTreeRecursively(group, group, rootGroupNode); } else { var brickRefsAttribute = rootGroupNode.Attributes["brickRefs"]; if (brickRefsAttribute != null) { group.brickRefs = ParseUtils.StringToIntArray(brickRefsAttribute.Value); SetGroupBricksFromBrickRefs(lxfml.bricks, group); } else { group.brickRefs = new int[0]; } } } } } } } catch (Exception e) { Debug.LogWarning(e + "\t" + e.StackTrace); success = false; } } return(success); }
public static void ReimportModelGroup(LXFMLDoc lxfml, ModelGroup group, ModelGroupImportSettings importSettings, bool detectConnectivity = false) { // Assign the new group import settings to the group. group.importSettings = importSettings; // We assume that the group can be found, so reimport it. if (group.processed) { // Remove all processed meshes. var renderers = group.GetComponentsInChildren <MeshRenderer>(); foreach (var renderer in renderers) { // FIXME Destroy the mesh? Prevents undo.. var filter = renderer.GetComponent <MeshFilter>(); //Undo.DestroyObjectImmediate(filter.sharedMesh); if (renderer.GetComponent <ModelGroup>() == null) { // Destroy submesh game objects entirely. Undo.DestroyObjectImmediate(renderer.gameObject); } else { // Destroy mesh related components on group game object. Object.DestroyImmediate(filter); Object.DestroyImmediate(renderer); } } } // FIXME Check if bricks are referenced. // FIXME Check if bricks have custom components attached. // Remove group bricks. var existingBricks = group.GetComponentsInChildren <Brick>(); foreach (var brick in existingBricks) { Undo.DestroyObjectImmediate(brick.gameObject); } var groupLightMapped = group.importSettings.isStatic && group.importSettings.lightmapped; SetStaticAndGIParams(group.gameObject, group.importSettings.isStatic, groupLightMapped); // Move group to origo to ensure that bricks are instantiated in the correct positions. var originalGroupLocalPosition = group.transform.localPosition; var originalGroupLocalRotation = group.transform.localRotation; var originalGroupLocalScale = group.transform.localScale; var originalGroupParent = group.transform.parent; var originalGroupSiblingIndex = group.transform.GetSiblingIndex(); group.transform.SetParent(null); group.transform.localPosition = Vector3.zero; group.transform.localRotation = Quaternion.identity; group.transform.localScale = Vector3.one; // Create dictionary with just this group. var modelGroupImportSettingsDictionary = new DictionaryIntToModelGroupImportSettings(); modelGroupImportSettingsDictionary.Add(group.number, group.importSettings); // Instantiate group bricks. var resultBricks = new Dictionary <int, Brick>(lxfml.bricks.Count); InstantiateModelBricks(lxfml, modelGroupImportSettingsDictionary, ref resultBricks, group.number); // Assign bricks to group. foreach (var brick in resultBricks.Values) { brick.transform.SetParent(group.transform); } ModelGroupUtility.RecomputePivot(group, false, ModelGroupUtility.UndoBehavior.withoutUndo); // Move group back to original location. group.transform.SetParent(originalGroupParent); group.transform.SetSiblingIndex(originalGroupSiblingIndex); group.transform.localPosition = originalGroupLocalPosition; group.transform.localRotation = originalGroupLocalRotation; group.transform.localScale = originalGroupLocalScale; /*if (group.processed) * { * // Process the group again. * // FIXME Is this even a good idea? * if (group.type == GroupType.Environment || group.type == GroupType.Static) * { * Vector2Int vertCount = Vector2Int.zero; * Vector2Int triCount = Vector2Int.zero; * Vector2Int meshCount = Vector2Int.zero; * Vector2Int colliderCount = Vector2Int.zero; * ModelProcessor.ProcessModelGroup(group, ref vertCount, ref triCount, ref meshCount, ref colliderCount); * * Debug.Log($"Process result (before/after):\nVerts {vertCount.x}/{vertCount.y}, tris {triCount.x}/{triCount.y}, meshes {meshCount.x}/{meshCount.y}, colliders {colliderCount.x}/{colliderCount.y}"); * } * }*/ if (detectConnectivity && group.importSettings.connectivity) { var sceneBricks = new HashSet <Brick>(StageUtility.GetCurrentStageHandle().FindComponentsOfType <Brick>()); DetectConnectivity(new HashSet <Brick>(resultBricks.Values), sceneBricks); } EditorUtility.ClearProgressBar(); }
public static void ReimportModel(LXFMLDoc lxfml, Model model, DictionaryIntToModelGroupImportSettings importSettings) { var brickBuilding = SceneBrickBuilder.GetToggleBrickBuildingStatus(); if (brickBuilding) { SceneBrickBuilder.ToggleBrickBuilding(); } // FIXME Next version could include option to match groups up manually. var groups = model.GetComponentsInChildren <ModelGroup>(); for (var i = groups.Length - 1; i >= 0; i--) { var group = groups[i]; if (group.autoGenerated) { Undo.DestroyObjectImmediate(group.gameObject); } else if (group.number >= lxfml.groups.Length) { Debug.LogWarning("Group " + group.number + " " + group.groupName + " does not match up with files. Wiping."); Undo.DestroyObjectImmediate(group.gameObject); } } groups = model.GetComponentsInChildren <ModelGroup>(); var removedGroups = new List <LXFMLDoc.BrickGroup>(); for (var i = 0; i < lxfml.groups.Length; i++) { LXFMLDoc.BrickGroup group = lxfml.groups[i]; bool exists = false; for (var j = 0; j < groups.Length; j++) { if (groups[j].number == group.number) { exists = true; break; } } if (!exists) { removedGroups.Add(group); } } // Assign the new model import settings to the model. model.importSettings = new DictionaryIntToModelGroupImportSettings(importSettings); var bricksWithConnectivity = new HashSet <Brick>(); if (removedGroups.Count > 0) { var resultBricks = new Dictionary <int, Brick>(lxfml.bricks.Count); foreach (var group in removedGroups) { var number = group.number; InstantiateModelBricks(lxfml, model.importSettings, ref resultBricks, number); var groupGO = InstantiateModelGroup(group, number, model.gameObject, model.absoluteFilePath, model.relativeFilePath, ref resultBricks, false, importSettings[number]); groupGO.transform.position = model.transform.position; Undo.RegisterCreatedObjectUndo(groupGO, "Re-creating model group"); if (importSettings[number].connectivity) { bricksWithConnectivity.UnionWith(groupGO.GetComponentsInChildren <Brick>()); } } } foreach (var group in groups) { group.absoluteFilePath = model.absoluteFilePath; group.relativeFilePath = model.relativeFilePath; ReimportModelGroup(lxfml, group, importSettings[group.number]); if (group.importSettings.connectivity) { bricksWithConnectivity.UnionWith(group.GetComponentsInChildren <Brick>()); } } if (bricksWithConnectivity.Count > 0) { var sceneBricks = new HashSet <Brick>(StageUtility.GetCurrentStageHandle().FindComponentsOfType <Brick>()); DetectConnectivity(bricksWithConnectivity, sceneBricks); } if (brickBuilding) { SceneBrickBuilder.ToggleBrickBuilding(); } if (SceneBrickBuilder.GetAutoUpdateHierarchy()) { groups = model.GetComponentsInChildren <ModelGroup>(); var bricks = new HashSet <Brick>(); foreach (var group in groups) { if (group.importSettings.connectivity) { var groupBricks = group.GetComponentsInChildren <Brick>(); foreach (var brick in groupBricks) { bricks.Add(brick); } } } // On reimport, the model will be positioned weirdly compared to the rest of the scene, so we just ignore all other bricks ModelGroupUtility.RecomputeModelGroups(bricks); } EditorUtility.ClearProgressBar(); }
/// <summary> /// Instantiate all bricks and groups in an LXFML document /// </summary> /// <param name="lxfml">The LXFML document</param> /// <param name="nameOfObject">Path of the LXFML document</param> public static GameObject InstantiateModel(LXFMLDoc lxfml, string filePath, Model.Pivot pivot, DictionaryIntToModelGroupImportSettings importSettings) { //Create "root" LXFML gameobject GameObject parent = new GameObject(Path.GetFileNameWithoutExtension(filePath)); Undo.RegisterCreatedObjectUndo(parent, "Model"); parent.transform.position = Vector3.zero; var model = parent.AddComponent <Model>(); model.absoluteFilePath = filePath; model.relativeFilePath = PathUtils.GetRelativePath(Directory.GetCurrentDirectory(), filePath); model.pivot = pivot; model.importSettings = new DictionaryIntToModelGroupImportSettings(importSettings); EditorUtility.DisplayProgressBar("Importing", "Creating bricks.", 0.0f); var resultBricks = new Dictionary <int, Brick>(lxfml.bricks.Count); InstantiateModelBricks(lxfml, importSettings, ref resultBricks); EditorUtility.DisplayProgressBar("Importing", "Creating groups.", 0.8f); if (resultBricks.Count > 0) { var bricksWithConnectivity = new HashSet <Brick>(); var groups = lxfml.groups; for (int i = 0; i < groups.Length; i++) { var number = groups[i].number; GameObject groupParent = InstantiateModelGroup(groups[i], i, parent, filePath, model.relativeFilePath, ref resultBricks, false, importSettings[number]); if (importSettings[number].connectivity) { bricksWithConnectivity.UnionWith(groupParent.GetComponentsInChildren <Brick>()); } } if (bricksWithConnectivity.Count > 0) { var sceneBricks = new HashSet <Brick>(StageUtility.GetCurrentStageHandle().FindComponentsOfType <Brick>()); DetectConnectivity(bricksWithConnectivity, sceneBricks); } if (SceneBrickBuilder.GetAutoUpdateHierarchy()) { var bricks = new HashSet <Brick>(); foreach (var pair in resultBricks) { foreach (var part in pair.Value.parts) { if (part.connectivity) { bricks.Add(pair.Value); break; } } } // On import, the model will be positioned weirdly compared to the rest of the scene, so we just ignore all other bricks ModelGroupUtility.RecomputeModelGroups(bricks, false, ModelGroupUtility.UndoBehavior.withoutUndo); } } // Change the pivot. if (pivot != Model.Pivot.Original) { EditorUtility.DisplayProgressBar("Importing", "Computing bounds.", 0.9f); var bounds = ComputeBounds(parent.transform); var newPivot = bounds.center; switch (pivot) { case Model.Pivot.BottomCenter: { newPivot += -parent.transform.up * bounds.extents.y; break; } } foreach (Transform child in parent.transform) { child.position -= newPivot; } parent.transform.position += newPivot; } // Add LEGOModelAsset component. parent.AddComponent <LEGOModelAsset>(); EditorUtility.ClearProgressBar(); return(parent); }
/// <summary> /// Instantiate game objects for each brick in an LXFML-file /// </summary> /// <param name="lxfml">The LXFML-file</param> /// <param name="colliders">Add colliders to part</param> /// <param name="connectivity">Add connectivity to part</param> /// <param name="isStatic">Make the part static</param> /// <param name="lightmapped">Instantiate meshes with or without lightmap UVs</param> /// <param name="randomizeRotation">Slightly rotate rotation of part</param> /// <param name="preferLegacy">Choose legacy meshes if available</param> /// <param name="lod">Instantiate meshes of a certain LOD</param> /// <param name="resultBricks">Dictionary that contains brick component, using refID as key</param> /// <param name="groupNumber">If non-negative, only instantiate bricks from the specified group number</param> public static void InstantiateModelBricks(LXFMLDoc lxfml, DictionaryIntToModelGroupImportSettings importSettings, ref Dictionary <int, Brick> resultBricks, int groupNumber = -1) { for (var i = 0; i < lxfml.bricks.Count; ++i) { if (i % 200 == 0) { EditorUtility.DisplayProgressBar("Importing", "Creating bricks.", ((float)i / lxfml.bricks.Count) * 0.7f); } var brick = lxfml.bricks[i]; var group = FindGroup(lxfml, brick); // Discard bricks from other groups if group number is specified. if (groupNumber >= 0 && group != null && group.number != groupNumber) { continue; } // Determine whether or not to be static and to generate light map UVs. var brickStatic = (group != null ? importSettings[group.number].isStatic : false); var brickLightmapped = brickStatic && (group != null ? importSettings[group.number].lightmapped : false); var brickLod = (group != null ? importSettings[group.number].lod : 0); var brickGO = new GameObject(brick.designId, typeof(Brick)); var brickComp = brickGO.GetComponent <Brick>(); Undo.RegisterCreatedObjectUndo(brickGO, "Brick"); foreach (var part in brick.parts) { GameObject partToInstantiate = null; var partExistenceResult = PartUtility.UnpackPart(part.partDesignId, brickLightmapped, group != null ? importSettings[group.number].preferLegacy : false, brickLod); if (partExistenceResult.existence != PartUtility.PartExistence.None) { // FIXME Make a note of changed design ids. partToInstantiate = PartUtility.LoadPart(partExistenceResult.designID, brickLightmapped, partExistenceResult.existence == PartUtility.PartExistence.Legacy, brickLod); } if (partToInstantiate == null) { Debug.LogError("Missing part FBX -> " + partExistenceResult.designID); continue; } var partGO = Object.Instantiate(partToInstantiate); partGO.name = partToInstantiate.name; // Assign legacy, material IDs and set up references. var partComp = partGO.AddComponent <Part>(); partComp.designID = Convert.ToInt32(part.partDesignId); partComp.legacy = partExistenceResult.existence == PartUtility.PartExistence.Legacy; foreach (var material in part.materials) { partComp.materialIDs.Add(material.colorId); } partComp.brick = brickComp; brickComp.parts.Add(partComp); if (partExistenceResult.existence == PartUtility.PartExistence.New) { // FIXME Handle normal mapped model. InstantiateKnobsAndTubes(partComp, brickLightmapped, brickLod); } // Create collider and connectivity information. var brickColliders = (group != null ? importSettings[group.number].colliders : false); var brickConnectivity = brickColliders && (group != null ? importSettings[group.number].connectivity : false); if (brickColliders) { GameObject collidersToInstantiate = null; var collidersAvailable = PartUtility.UnpackCollidersForPart(partExistenceResult.designID); if (collidersAvailable) { collidersToInstantiate = PartUtility.LoadCollidersPrefab(partExistenceResult.designID); } if (collidersToInstantiate == null && partExistenceResult.existence != PartUtility.PartExistence.Legacy) { Debug.LogError("Missing part collider information -> " + partExistenceResult.designID); } if (collidersToInstantiate) { var collidersGO = Object.Instantiate(collidersToInstantiate); collidersGO.name = "Colliders"; collidersGO.transform.SetParent(partGO.transform, false); var colliderComps = collidersGO.GetComponentsInChildren <Collider>(); partComp.colliders.AddRange(colliderComps); } } if (brickConnectivity) { GameObject connectivityToInstantiate = null; var connectivityAvailable = PartUtility.UnpackConnectivityForPart(partExistenceResult.designID); if (connectivityAvailable) { connectivityToInstantiate = PartUtility.LoadConnectivityPrefab(partExistenceResult.designID); } if (connectivityToInstantiate == null && partExistenceResult.existence != PartUtility.PartExistence.Legacy) { Debug.LogError("Missing part connectivity information -> " + partExistenceResult.designID); } if (connectivityToInstantiate) { var connectivityGO = Object.Instantiate(connectivityToInstantiate); connectivityGO.name = "Connectivity"; connectivityGO.transform.SetParent(partGO.transform, false); var connectivityComp = connectivityGO.GetComponent <Connectivity>(); partComp.connectivity = connectivityComp; brickComp.totalBounds.Encapsulate(connectivityComp.extents); connectivityComp.part = partComp; foreach (var field in connectivityComp.connectionFields) { foreach (var connection in field.connections) { MatchConnectionWithKnob(connection, partComp.knobs); MatchConnectionWithTubes(connection, partComp.tubes); } } } } SetMaterials(partComp, part.materials, partExistenceResult.existence == PartUtility.PartExistence.Legacy); SetDecorations(partComp, part.decorations, partExistenceResult.existence == PartUtility.PartExistence.Legacy); SetStaticAndGIParams(partGO, brickStatic, brickLightmapped, true); // Set Position & Rotation SetPositionRotation(partGO, part); if (group != null ? importSettings[group.number].randomizeRotation : false) { RandomizeRotation(partComp, brickConnectivity); } // If first part, place brick at same position. if (brickGO.transform.childCount == 0) { brickGO.transform.position = partGO.transform.position; brickGO.transform.rotation = partGO.transform.rotation; brickGO.transform.localScale = Vector3.one; } partGO.transform.SetParent(brickGO.transform, true); if (!brickConnectivity) { var worldBounds = ComputeBounds(partGO.transform); worldBounds.SetMinMax(brickComp.transform.InverseTransformPoint(worldBounds.min), brickComp.transform.InverseTransformPoint(worldBounds.max)); brickComp.totalBounds.Encapsulate(worldBounds); } } // If all parts were missing, discard brick. if (brickGO.transform.childCount == 0) { Undo.DestroyObjectImmediate(brickGO); continue; } SetStaticAndGIParams(brickGO, brickStatic, brickLightmapped); // Assign uuid brickComp.designID = Convert.ToInt32(brick.designId); brickComp.uuid = brick.uuid; // Add LEGOAsset component. brickGO.AddComponent <LEGOAsset>(); resultBricks[brick.refId] = brickComp; } }
public static void ReimportModel(Model model, string newReimportPath = null) { var absoluteFilePath = model.absoluteFilePath; var relativeFilePath = model.relativeFilePath; // Update the file path if (newReimportPath != null) { absoluteFilePath = newReimportPath; relativeFilePath = PathUtils.GetRelativePath(Directory.GetCurrentDirectory(), newReimportPath); } lxfml = ReadFileLogic(relativeFilePath); if (lxfml == null) { lxfml = ReadFileLogic(absoluteFilePath); } if (lxfml != null) { pivot = model.pivot; // Store the new reimport path so it can be applied to the model if reimport is successful. filePath = newReimportPath; importSettings.Clear(); // Make sure we are compatible with models from before ModelGroupImportSettings by adding default import settings. if (model.importSettings.Keys.Count == 0) { foreach (var group in lxfml.groups) { importSettings.Add(group.number, new ModelGroupImportSettings()); } } else { foreach (var entry in model.importSettings) { importSettings.Add(entry.Key, entry.Value); } } foreach (var group in lxfml.groups) { if (!importSettings.ContainsKey(group.number)) { importSettings.Add(group.number, new ModelGroupImportSettings()); } } // Check if groups match up with the file. // FIXME Next version could include option to match groups up manually. // FIXME Check on group name? We do not have direct access to the groups from the model. var groupsMatch = true; foreach (var entry in importSettings) { if (entry.Key >= lxfml.groups.Length) { Debug.LogWarning("Group " + entry.Key + " does not match up with file."); groupsMatch = false; } } if (!groupsMatch) { EditorUtility.DisplayDialog("Reimport failed", "Model groups do not match up with groups in file. Check log for details", "Ok"); return; } ImportModel.model = model; group = null; GetWindow <ImportModel>(true, "LEGO Model Importer"); } else { ShowReadError(); } }
private static LXFMLDoc ReadFileLogic(string path) { var extension = Path.GetExtension(path).ToLowerInvariant(); // Reset tracked errors. trackedErrors = new Dictionary <string, List <string> >(); XmlDocument doc = null; switch (extension) { case ".lxfml": { doc = new XmlDocument(); doc.LoadXml(File.ReadAllText(path)); break; } case ".lxf": { // Open LXF file. using (var lxfArchive = ZipFile.OpenRead(path)) { var entry = lxfArchive.GetEntry("IMAGE100.LXFML"); if (entry != null) { doc = new XmlDocument(); var lxfmlStream = entry.Open(); doc.Load(lxfmlStream); lxfmlStream.Dispose(); } } break; } case ".ldr": { var ldrStream = new FileStream(path, FileMode.Open); doc = LDrawConverter.ConvertLDrawToLXFML(ldrStream, path); ldrStream.Dispose(); trackedErrors = LDrawConverter.GetErrors(); break; } case ".io": { // Cannot open IO file. break; } } if (doc != null) { var lxfml = new LXFMLDoc(); if (LXFMLReader.ReadLxfml(doc, ref lxfml)) { if (lxfml.groups == null) { Debug.Log("No groups in " + path + " Creating default group."); CreateDefaultGroup(lxfml); } else if (FixLooseBricks(lxfml)) { Debug.Log("Found bricks with no group. Adding them to main model."); } return(lxfml); } } return(null); }
public static void ReimportModelGroup(LXFMLDoc lxfml, ModelGroup group, ModelGroupImportSettings importSettings, bool detectConnectivity = false) { // Assign the new group import settings to the group. group.importSettings = importSettings; // We assume that the group can be found, so reimport it. if (group.processed) { // Remove all processed meshes. var renderers = group.GetComponentsInChildren <MeshRenderer>(); foreach (var renderer in renderers) { // FIXME Destroy the mesh? Prevents undo.. var filter = renderer.GetComponent <MeshFilter>(); //Undo.DestroyObjectImmediate(filter.sharedMesh); if (renderer.GetComponent <ModelGroup>() == null) { // Destroy submesh game objects entirely. Undo.DestroyObjectImmediate(renderer.gameObject); } else { // Destroy mesh related components on group game object. Object.DestroyImmediate(filter); Object.DestroyImmediate(renderer); } } } // FIXME Check if bricks are referenced. // FIXME Check if bricks have custom components attached. // Remove group bricks. var existingBricks = group.GetComponentsInChildren <Brick>(); foreach (var brick in existingBricks) { Undo.DestroyObjectImmediate(brick.gameObject); } var groupLightMapped = group.importSettings.isStatic && group.importSettings.lightmapped; SetStaticAndGIParams(group.gameObject, group.importSettings.isStatic, groupLightMapped); // Move group to origo to ensure that bricks are instantiated in the correct positions. var originalGroupParent = group.transform.parent; var originalGroupSiblingIndex = group.transform.GetSiblingIndex(); group.transform.parent = null; group.transform.localPosition = Vector3.zero; group.transform.localRotation = Quaternion.identity; group.transform.localScale = Vector3.one; // Create dictionary with just this group. var modelGroupImportSettingsDictionary = new DictionaryIntToModelGroupImportSettings(); modelGroupImportSettingsDictionary.Add(group.number, group.importSettings); // Instantiate group bricks. var resultBricks = new Dictionary <int, Brick>(lxfml.bricks.Count); InstantiateModelBricks(lxfml, modelGroupImportSettingsDictionary, ref resultBricks, group.number); // Assign bricks to group. foreach (var brick in resultBricks.Values) { brick.transform.SetParent(group.transform); } // Set parent of group back to original. group.transform.parent = originalGroupParent; group.transform.SetSiblingIndex(originalGroupSiblingIndex); if (detectConnectivity && group.importSettings.connectivity) { var sceneBricks = new HashSet <Brick>(StageUtility.GetCurrentStageHandle().FindComponentsOfType <Brick>()); DetectConnectivity(new HashSet <Brick>(resultBricks.Values), sceneBricks); } EditorUtility.ClearProgressBar(); }
public static void ReimportModel(LXFMLDoc lxfml, Model model, Model.Pivot previousPivot, DictionaryIntToModelGroupImportSettings importSettings) { var brickBuilding = SceneBrickBuilder.GetToggleBrickBuildingStatus(); if (brickBuilding) { SceneBrickBuilder.ToggleBrickBuilding(); } // FIXME Next version could include option to match groups up manually. var oldPosition = model.transform.position; var oldRotation = model.transform.rotation; model.transform.position = Vector3.zero; model.transform.rotation = Quaternion.identity; var groups = model.GetComponentsInChildren <ModelGroup>(); for (var i = groups.Length - 1; i >= 0; i--) { var group = groups[i]; if (group.autoGenerated) { Undo.DestroyObjectImmediate(group.gameObject); } else if (group.number >= lxfml.groups.Length) { Debug.LogWarning("Group " + group.number + " " + group.groupName + " does not match up with files. Wiping."); Undo.DestroyObjectImmediate(group.gameObject); } } groups = model.GetComponentsInChildren <ModelGroup>(); var removedGroups = new List <LXFMLDoc.BrickGroup>(); for (var i = 0; i < lxfml.groups.Length; i++) { LXFMLDoc.BrickGroup group = lxfml.groups[i]; bool exists = false; for (var j = 0; j < groups.Length; j++) { if (groups[j].number == group.number) { exists = true; break; } } if (!exists) { removedGroups.Add(group); } } // Assign the new model import settings to the model. model.importSettings = new DictionaryIntToModelGroupImportSettings(importSettings); var bricksWithConnectivity = new HashSet <Brick>(); if (removedGroups.Count > 0) { var resultBricks = new Dictionary <int, Brick>(lxfml.bricks.Count); foreach (var group in removedGroups) { var number = group.number; InstantiateModelBricks(lxfml, model.importSettings, ref resultBricks, number); var groupGO = InstantiateModelGroup(group, number, model.gameObject, model.absoluteFilePath, model.relativeFilePath, ref resultBricks, false, importSettings[number]); groupGO.transform.position = model.transform.position; Undo.RegisterCreatedObjectUndo(groupGO, "Re-creating model group"); if (importSettings[number].connectivity) { bricksWithConnectivity.UnionWith(groupGO.GetComponentsInChildren <Brick>()); } } } foreach (var group in groups) { group.absoluteFilePath = model.absoluteFilePath; group.relativeFilePath = model.relativeFilePath; ReimportModelGroup(lxfml, group, importSettings[group.number]); if (group.importSettings.connectivity) { bricksWithConnectivity.UnionWith(group.GetComponentsInChildren <Brick>()); } } if (bricksWithConnectivity.Count > 0) { var sceneBricks = new HashSet <Brick>(StageUtility.GetCurrentStageHandle().FindComponentsOfType <Brick>()); DetectConnectivity(bricksWithConnectivity, sceneBricks); } if (SceneBrickBuilder.GetAutoUpdateHierarchy()) { groups = model.GetComponentsInChildren <ModelGroup>(); var bricks = new HashSet <Brick>(); foreach (var group in groups) { if (group.importSettings.connectivity) { var groupBricks = group.GetComponentsInChildren <Brick>(); foreach (var brick in groupBricks) { bricks.Add(brick); } } } ModelGroupUtility.RecomputeHierarchy(bricks, false, ModelGroupUtility.UndoBehavior.withUndo); var oldToNew = model.transform.position; model.transform.rotation = oldRotation; model.transform.position = oldPosition; oldToNew = model.transform.TransformVector(oldToNew); if ((previousPivot == Model.Pivot.Center || previousPivot == Model.Pivot.BottomCenter) && model.pivot == Model.Pivot.Original) { ModelGroupUtility.RecomputePivot(model.transform, previousPivot, true, ModelGroupUtility.UndoBehavior.withoutUndo); var oldPivotPos = model.transform.position; model.transform.position = oldPosition; var diff = model.transform.position - oldPivotPos; model.transform.position += diff; foreach (var brick in model.GetComponentsInChildren <Brick>()) { brick.transform.position -= diff; } model.transform.rotation = oldRotation; } if (model.pivot != Model.Pivot.Original && previousPivot == Model.Pivot.Original) { model.transform.position += oldToNew; model.transform.rotation = oldRotation; } if (model.pivot != Model.Pivot.Original && previousPivot != Model.Pivot.Original) { if (model.pivot != previousPivot) { ModelGroupUtility.RecomputePivot(model.transform, previousPivot, true, ModelGroupUtility.UndoBehavior.withoutUndo); model.transform.position = oldPosition; ModelGroupUtility.RecomputePivot(model, false, ModelGroupUtility.UndoBehavior.withoutUndo); model.transform.rotation = oldRotation; } } } if (brickBuilding) { SceneBrickBuilder.ToggleBrickBuilding(); } EditorUtility.ClearProgressBar(); }