// This method is used by Component specific classes (e.g. ChiselBoxEditor) // to create a menu Item that creates a gameObject with a specific component (e.g. ChiselBox) // Since MenuItems can only be created with attributes, and strings must be constant, // we can only do this from place where we specifically know which component the menu is for. protected static void CreateAsGameObjectMenuCommand(MenuCommand menuCommand, string name) { var component = ChiselComponentFactory.Create <T>(name, ChiselModelManager.GetActiveModelOrCreate()); var gameObject = component.gameObject; GameObjectUtility.SetParentAndAlign(gameObject, menuCommand.context as GameObject); Undo.RegisterCreatedObjectUndo(gameObject, "Create " + gameObject.name); Selection.activeObject = gameObject; }
private static void ImportValveMapFormat2006() { GameObject go = null; try { string path = EditorUtility.OpenFilePanel("Import Source Engine Map", "", "vmf"); if (path.Length != 0) { EditorUtility.DisplayProgressBar("Chisel: Importing Source Engine Map", "Parsing Valve Map Format File (*.vmf)...", 0.0f); var importer = new ValveMapFormat2006.VmfImporter(); var map = importer.Import(path); // create parent game object to store all of the imported content. go = new GameObject("Source Map - " + Path.GetFileNameWithoutExtension(path)); go.SetActive(false); // create chisel model and import all of the brushes. EditorUtility.DisplayProgressBar("Chisel: Importing Source Engine Map", "Preparing Material Searcher...", 0.0f); ValveMapFormat2006.VmfWorldConverter.Import(ChiselModelManager.CreateNewModel(go.transform), map); #if COM_AETERNUMGAMES_CHISEL_DECALS // optional decals package: https://github.com/Henry00IS/Chisel.Decals // rebuild the world as we need the collision mesh for decals. EditorUtility.DisplayProgressBar("Chisel: Importing Source Engine Map", "Rebuilding the world...", 0.5f); go.SetActive(true); ChiselNodeHierarchyManager.Rebuild(); #endif // begin converting hammer entities to unity objects. ValveMapFormat2006.VmfEntityConverter.Import(go.transform, map); } } catch (Exception ex) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("Source Engine Map Import", "An exception occurred while importing the map:\r\n" + ex.Message, "Ohno!"); } finally { EditorUtility.ClearProgressBar(); if (go != null) { go.SetActive(true); } } }
/// <summary> /// Imports the specified world into the SabreCSG model. /// </summary> /// <param name="model">The model to import into.</param> /// <param name="world">The world to be imported.</param> /// <param name="scale">The scale modifier.</param> public static void Import(Transform rootTransform, MapWorld world) { _conversionScale = 1.0f / _Scale; // create a material searcher to associate materials automatically. MaterialSearcher materialSearcher = new MaterialSearcher(); var mapTransform = CreateGameObjectWithUniqueName(world.mapName, rootTransform); mapTransform.position = Vector3.zero; // Index of entities by trenchbroom id var entitiesById = new Dictionary <int, EntityContainer>(); var layers = new List <EntityContainer>(); for (int e = 0; e < world.Entities.Count; e++) { var entity = world.Entities[e]; //EntityContainer eContainer = null; if (entity.tbId >= 0) { var name = String.IsNullOrEmpty(entity.tbName) ? "Unnamed" : entity.tbName; var t = CreateGameObjectWithUniqueName(name, mapTransform); var eContainer = new EntityContainer(t, entity); entitiesById.Add(entity.tbId, eContainer); if (entity.tbType == "_tb_layer") { layers.Add(eContainer); eContainer.transform.SetParent(null); // unparent until layers are sorted by sort index } } } var defaultLayer = CreateGameObjectWithUniqueName("Default Layer", mapTransform); layers = layers.OrderBy(l => l.entity.tbLayerSortIndex).ToList(); // sort layers by layer sort index foreach (var l in layers) { l.transform.SetParent(mapTransform); // parent layers to map in order } bool valveFormat = world.valveFormat; // iterate through all entities. for (int e = 0; e < world.Entities.Count; e++) { #if UNITY_EDITOR UnityEditor.EditorUtility.DisplayProgressBar("Importing Quake 1 Map", "Converting Quake 1 Entities To Brushes (" + (e + 1) + " / " + world.Entities.Count + ")...", e / (float)world.Entities.Count); #endif MapEntity entity = world.Entities[e]; Transform brushParent = mapTransform; bool isLayer = false; bool isTrigger = false; if (entity.ClassName == "worldspawn") { brushParent = defaultLayer; } else if (entity.tbType == "_tb_layer") { isLayer = true; if (entitiesById.TryGetValue(entity.tbId, out EntityContainer eContainer)) { brushParent = eContainer.transform; } } else if (entity.tbType == "_tb_group") { if (entitiesById.TryGetValue(entity.tbId, out EntityContainer eContainer)) { brushParent = eContainer.transform; } } else { if (entity.ClassName.Contains("trigger")) { isTrigger = true; } brushParent = CreateGameObjectWithUniqueName(entity.ClassName, mapTransform); } if (brushParent != mapTransform && brushParent != defaultLayer) { if (entity.tbGroup > 0) { if (entitiesById.TryGetValue(entity.tbGroup, out EntityContainer eContainer)) { brushParent.SetParent(eContainer.transform); } } else if (entity.tbLayer > 0) { if (entitiesById.TryGetValue(entity.tbLayer, out EntityContainer eContainer)) { brushParent.SetParent(eContainer.transform); } } else if (!isLayer) { brushParent.SetParent(defaultLayer); } } //if(entity.) if (entity.Brushes.Count == 0) { continue; } var model = ChiselModelManager.CreateNewModel(brushParent); // var model = OperationsUtility.CreateModelInstanceInScene(brushParent); var parent = model.transform; if (isTrigger) { //model.Settings = (model.Settings | ModelSettingsFlags.IsTrigger | ModelSettingsFlags.SetColliderConvex | ModelSettingsFlags.DoNotRender); } // iterate through all entity brushes. for (int i = 0; i < entity.Brushes.Count; i++) { MapBrush brush = entity.Brushes[i]; // build a very large cube brush. ChiselBrush go = ChiselComponentFactory.Create <ChiselBrush>(model); go.definition.surfaceDefinition = new ChiselSurfaceDefinition(); go.definition.surfaceDefinition.EnsureSize(6); BrushMesh brushMesh = new BrushMesh(); go.definition.brushOutline = brushMesh; BrushMeshFactory.CreateBox(ref brushMesh, new Vector3(-4096, -4096, -4096), new Vector3(4096, 4096, 4096), in go.definition.surfaceDefinition); // prepare for uv calculations of clip planes after cutting. var planes = new float4[brush.Sides.Count]; var planeSurfaces = new ChiselSurface[brush.Sides.Count]; // compute all the sides of the brush that will be clipped. for (int j = brush.Sides.Count; j-- > 0;) { MapBrushSide side = brush.Sides[j]; // detect excluded polygons. //if (IsExcludedMaterial(side.Material)) //polygon.UserExcludeFromFinal = true; // detect collision-only brushes. //if (IsInvisibleMaterial(side.Material)) //pr.IsVisible = false; // find the material in the unity project automatically. Material material; string materialName = side.Material.Replace("*", "#"); material = materialSearcher.FindMaterial(new string[] { materialName }); if (material == null) { material = ChiselMaterialManager.DefaultFloorMaterial; } // create chisel surface for the clip. ChiselSurface surface = new ChiselSurface(); surface.brushMaterial = ChiselBrushMaterial.CreateInstance(material, ChiselMaterialManager.DefaultPhysicsMaterial); surface.surfaceDescription = SurfaceDescription.Default; // detect collision-only polygons. if (IsInvisibleMaterial(side.Material)) { surface.brushMaterial.LayerUsage &= ~LayerUsageFlags.RenderReceiveCastShadows; } // detect excluded polygons. if (IsExcludedMaterial(side.Material)) { surface.brushMaterial.LayerUsage &= LayerUsageFlags.CastShadows; surface.brushMaterial.LayerUsage |= LayerUsageFlags.Collidable; } // calculate the clipping planes. Plane clip = new Plane(go.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) * _conversionScale), go.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) * _conversionScale), go.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) * _conversionScale)); planes[j] = new float4(clip.normal, clip.distance); planeSurfaces[j] = surface; } // cut all the clipping planes out of the brush in one go. brushMesh.Cut(planes, planeSurfaces); // now iterate over the planes to calculate UV coordinates. int[] indices = new int[brush.Sides.Count]; for (int k = 0; k < planes.Length; k++) { var plane = planes[k]; int closestIndex = 0; float closestDistance = math.lengthsq(plane - brushMesh.planes[0]); for (int j = 1; j < brushMesh.planes.Length; j++) { float testDistance = math.lengthsq(plane - brushMesh.planes[j]); if (testDistance < closestDistance) { closestIndex = j; closestDistance = testDistance; } } indices[k] = closestIndex; } for (int j = 0; j < indices.Length; j++) { brushMesh.planes[indices[j]] = planes[j]; } for (int j = brush.Sides.Count; j-- > 0;) { MapBrushSide side = brush.Sides[j]; var surface = brushMesh.polygons[indices[j]].surface; var material = surface.brushMaterial.RenderMaterial; // calculate the texture coordinates. int w = 256; int h = 256; if (material.mainTexture != null) { w = material.mainTexture.width; h = material.mainTexture.height; } var clip = new Plane(planes[j].xyz, planes[j].w); if (world.valveFormat) { var uAxis = new VmfAxis(side.t1, side.Offset.X, side.Scale.X); var vAxis = new VmfAxis(side.t2, side.Offset.Y, side.Scale.Y); CalculateTextureCoordinates(go, surface, clip, w, h, uAxis, vAxis); } else { if (GetTextureAxises(clip, out MapVector3 t1, out MapVector3 t2)) { var uAxis = new VmfAxis(t1, side.Offset.X, side.Scale.X); var vAxis = new VmfAxis(t2, side.Offset.Y, side.Scale.Y); CalculateTextureCoordinates(go, surface, clip, w, h, uAxis, vAxis); } } } try { // finalize the brush by snapping planes and centering the pivot point. go.transform.position += brushMesh.CenterAndSnapPlanes(); } catch { // Brush failed, destroy brush GameObject.DestroyImmediate(go); } } } #if UNITY_EDITOR UnityEditor.EditorUtility.ClearProgressBar(); #endif }
// This method is used by Component specific classes (e.g. ChiselBoxEditor) // to create a menu Item that creates a gameObject with a specific component (e.g. ChiselBox) // Since MenuItems can only be created with attributes, and strings must be constant, // we can only do this from place where we specifically know which component the menu is for. protected static void CreateAsGameObjectMenuCommand(MenuCommand menuCommand, string name) { T component; // If we use the command object on a gameobject in the hierarchy, choose that gameobject // Otherwise: choose the activeModel (if available) var context = (menuCommand.context as GameObject); var parentGameObject = (context != null) ? context : (ChiselModelManager.ActiveModel != null) ? ChiselModelManager.ActiveModel.gameObject : null; var parentTransform = (parentGameObject == null) ? null : parentGameObject.transform; // If we used the command object on a generator, choose it's parent to prevent us from // adding a generator as a child to a generator if (parentTransform && parentTransform.GetComponent <ChiselGeneratorComponent>()) { parentTransform = parentTransform.parent; parentGameObject = (parentTransform == null) ? null : parentTransform.gameObject; } // Create the gameobject if (parentTransform) { component = ChiselComponentFactory.Create <T>(name, parentTransform); } else { component = ChiselComponentFactory.Create <T>(name); } var gameObject = component.gameObject; GameObjectUtility.SetParentAndAlign(gameObject, parentGameObject); Undo.RegisterCreatedObjectUndo(gameObject, "Create " + gameObject.name); // Find the appropriate model to make active after we created the generator ChiselModel model; if (typeof(T) != typeof(ChiselModel)) { model = gameObject.GetComponentInParent <ChiselModel>(); // If we don't have a parent model, create one and put the generator underneath it if (!model) { model = ChiselModelManager.CreateNewModel(gameObject.transform.parent); // Make sure we create the model at the exact same location as the generator var modelGameObject = model.gameObject; var modelTransform = model.transform; var childSiblingIndex = modelTransform.GetSiblingIndex(); modelTransform.SetSiblingIndex(childSiblingIndex); Undo.RegisterCreatedObjectUndo(modelGameObject, "Create " + modelGameObject.name); MoveTargetsUnderModel(new[] { component }, model); } } else { model = component as ChiselModel; } // Set the active model before we select the gameobject, otherwise we'll be selecting the model instead ChiselModelManager.ActiveModel = model; Selection.activeObject = gameObject; }
private static void OnWillFlushUndoRecord() { ChiselModelManager.OnWillFlushUndoRecord(); }
private static void OnActiveSceneChanged(Scene prevScene, Scene newScene) { ChiselModelManager.OnActiveSceneChanged(prevScene, newScene); }