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(); } }
/// <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.RecomputeHierarchy(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; } } var difference = parent.transform.position - newPivot; foreach (Transform child in parent.transform) { child.position -= difference; } parent.transform.position = newPivot; } // Add LEGOModelAsset component. parent.AddComponent <LEGOModelAsset>(); EditorUtility.ClearProgressBar(); return(parent); }
private void OnGUI() { // Find max label width. var maxLabelWidth = 110.0f; for (int i = 0; i < lxfml.groups.Length; i++) { var size = EditorStyles.boldLabel.CalcSize(new GUIContent(lxfml.groups[i].name)); maxLabelWidth = Mathf.Max(maxLabelWidth, size.x); } minSize = new Vector2(leftMargin + maxLabelWidth + 360, 226); maxSize = new Vector2(leftMargin + maxLabelWidth + 360, 2000); scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, false); GUI.Box(new Rect(20, 20, 100, 100), logoTexture); CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 20.0f, 100.0f), "Colliders", "Add colliders to bricks."); CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 50.0f, 100.0f), "Connectivity", "Add connectivity to bricks. Connectivity requires colliders."); CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 100.0f, 100.0f), "Static", "Make bricks static."); CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 130.0f, 100.0f), "Lightmapped", "Add lightmap UVs to bricks. Bricks must be static to be lightmapped."); CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 180.0f, 100.0f), "Randomize Rotation", "Slightly rotate bricks to improve realism."); CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 230.0f, 100.0f), "Prefer Legacy", "Prefer legacy geometry over new geometry."); CreateHeaderUI(new Vector2(leftMargin + maxLabelWidth + 280.0f, 100.0f), "LOD", "LOD 0 includes chamfered edges.\nLOD 1 does not.\nLOD 2 simplifies knobs."); // Reserve the space for the GUILayout scroll view. GUILayout.Space(135.0f); var nextY = 135.0f; var showAllBoolsUI = model == null && group == null && lxfml.groups.Length > 1; // When importing a new model, just check if there is more than one lxfml group. showAllBoolsUI |= model != null && importSettings.Count > 1; // When reimporting an entire model, check if the existing model has more than one group. if (showAllBoolsUI) { CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 15.0f, nextY), importSettings, "colliders", "connectivity", null); CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 45.0f, nextY), importSettings, "connectivity", null, "colliders"); CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 95.0f, nextY), importSettings, "isStatic", "lightmapped", null); CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 125.0f, nextY), importSettings, "lightmapped", null, "isStatic"); CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 175.0f, nextY), importSettings, "randomizeRotation"); CreateAllBoolsUI(new Vector2(leftMargin + maxLabelWidth + 225.0f, nextY), importSettings, "preferLegacy"); CreateAllLODsUI(new Vector2(leftMargin + maxLabelWidth + 275.0f, nextY), importSettings); // Reserve the space for the GUILayout scroll view. GUILayout.Space(25.0f); nextY += 25.0f; } var collidersOrConnectivityWhilePreferringLegacy = false; for (int i = 0; i < lxfml.groups.Length; i++) { var showGroup = group == null && model == null; // When importing a new model, show all groups. showGroup |= model != null && importSettings.ContainsKey(i); // When reimporting an entire model, only show groups already in the existing model. showGroup |= group != null && i == group.number; // When reimporting a model group, only show that group. if (showGroup) { GUI.Label(new Rect(leftMargin, nextY, maxLabelWidth, 20.0f), lxfml.groups[i].name); CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 15.0f, nextY), importSettings, "colliders", i, "connectivity", null); CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 45.0f, nextY), importSettings, "connectivity", i, null, "colliders"); CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 95.0f, nextY), importSettings, "isStatic", i, "lightmapped", null); CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 125.0f, nextY), importSettings, "lightmapped", i, null, "isStatic"); CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 175.0f, nextY), importSettings, "randomizeRotation", i); CreateBoolUI(new Vector2(leftMargin + maxLabelWidth + 225.0f, nextY), importSettings, "preferLegacy", i); CreateLODUI(new Vector2(leftMargin + maxLabelWidth + 275.0f, nextY), importSettings, i); if ((importSettings[i].colliders || importSettings[i].connectivity) && importSettings[i].preferLegacy) { collidersOrConnectivityWhilePreferringLegacy = true; } // Reserve the space for the GUILayout scroll view. GUILayout.Space(20.0f); nextY += 20.0f; } } if (collidersOrConnectivityWhilePreferringLegacy) { EditorGUI.HelpBox(new Rect(leftMargin, nextY, position.width - leftMargin - 20.0f, 38.0f), "Legacy parts might not contain colliders or connectivity information.", MessageType.Warning); // Reserve the space for the GUILayout scroll view. GUILayout.Space(42.0f); nextY += 42.0f; } // Reserve the space for the GUILayout scroll view. GUILayout.Space(5.0f); nextY += 5.0f; // Only show pivot option when not reimporting. if (group == null && model == null) { GUI.Label(new Rect(leftMargin, nextY, maxLabelWidth, 20.0f), "Pivot"); pivot = (Model.Pivot)EditorGUI.EnumPopup(new Rect(leftMargin + maxLabelWidth + 15.0f, nextY, 126.0f, 16.0f), pivot); // Reserve the space for the GUILayout scroll view. GUILayout.Space(25.0f); nextY += 25.0f; } // Create the right import/reimport button and handle the import/reimport based on the three cases: // - Reimport model // - Reimport model group // - Import model bool importPressed; if (model) { // ---------------------- // Reimport entire model. // ---------------------- importPressed = GUI.Button(new Rect(leftMargin, nextY, maxLabelWidth + 15.0f + 126.0f, 32.0f), "Reimport Model"); if (importPressed) { // Register undo. Undo.RegisterFullObjectHierarchyUndo(model.gameObject, "Reimport"); model.pivot = pivot; // Update the path if it is new. if (filePath != null) { model.absoluteFilePath = filePath; model.relativeFilePath = PathUtils.GetRelativePath(Directory.GetCurrentDirectory(), filePath); } ModelImporter.ReimportModel(lxfml, model, importSettings); var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); if (prefabStage != null) { EditorSceneManager.MarkSceneDirty(prefabStage.scene); } SceneBrickBuilder.MarkSceneDirty(); } } else if (group) { // --------------------- // Reimport model group. // --------------------- importPressed = GUI.Button(new Rect(leftMargin, nextY, maxLabelWidth + 15.0f + 126.0f, 32.0f), "Reimport Model Group"); if (importPressed) { // Register undo. Undo.RegisterFullObjectHierarchyUndo(group.gameObject, "Reimport"); ModelImporter.ReimportModelGroup(lxfml, group, importSettings[group.number], true); var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); if (prefabStage != null) { EditorSceneManager.MarkSceneDirty(prefabStage.scene); } SceneBrickBuilder.MarkSceneDirty(); } } else { // ---------------------- // Import model. // ---------------------- importPressed = GUI.Button(new Rect(leftMargin, nextY, maxLabelWidth + 15.0f + 126.0f, 32.0f), "Import Model"); if (importPressed) { model = ModelImporter.InstantiateModel(lxfml, filePath, pivot, importSettings).GetComponent <Model>(); } var camera = SceneView.lastActiveSceneView.camera; if (camera && model) { var cameraRay = new Ray(camera.transform.position, camera.transform.forward); PhysicsScene physicsScene; if (PrefabStageUtility.GetCurrentPrefabStage() != null) { physicsScene = PrefabStageUtility.GetCurrentPrefabStage().scene.GetPhysicsScene(); } else { physicsScene = PhysicsSceneExtensions.GetPhysicsScene(EditorSceneManager.GetActiveScene()); } var bricksInModel = model.GetComponentsInChildren <Brick>(); var bricks = new HashSet <Brick>(bricksInModel); var sourceBrick = bricks.First(); BrickBuildingUtility.AlignBricks(sourceBrick, bricks, BrickBuildingUtility.ComputeBounds(bricks), sourceBrick.transform.position, Vector3.zero, cameraRay, new Plane(Vector3.up, Vector3.zero), 100.0f, out Vector3 offset, out Vector3 alignedOffset, out _, out _); var offsetPosition = model.transform.position + alignedOffset; model.transform.position = offsetPosition; Selection.activeGameObject = model.gameObject; Physics.SyncTransforms(); } SceneBrickBuilder.MarkSceneDirty(); } // Reserve the space for the GUILayout scroll view. GUILayout.Space(36.0f); nextY += 36.0f; // List tracked errors. foreach (var trackedError in trackedErrors) { EditorGUI.HelpBox(new Rect(leftMargin, nextY, maxLabelWidth + 340.0f, 38.0f), trackedError.Key, MessageType.Warning); // Reserve the space for the GUILayout scroll view. GUILayout.Space(42.0f); nextY += 42.0f; foreach (var id in trackedError.Value) { GUI.Label(new Rect(leftMargin, nextY, maxLabelWidth + 340.0f, 16.0f), id); // Reserve the space for the GUILayout scroll view. GUILayout.Space(20.0f); nextY += 20.0f; } } GUILayout.EndScrollView(); if (importPressed) { this.Close(); } }