/// <summary> /// Compares two build settings and returns if they are practially different. /// </summary> public static bool AreDifferent(CSGBuildSettings settings1, CSGBuildSettings settings2) { if (settings1.GenerateCollisionMeshes != settings2.GenerateCollisionMeshes) { return(true); } if (settings1.GenerateTangents != settings2.GenerateTangents) { return(true); } if (settings1.OptimizeGeometry != settings2.OptimizeGeometry) { return(true); } if (settings1.GenerateLightmapUVs != settings2.GenerateLightmapUVs) { return(true); } // Only compare UV unwrap settings if unwrapping is in use if (settings1.GenerateLightmapUVs && settings1.GenerateLightmapUVs) { if (settings1.UnwrapAngleError != settings2.UnwrapAngleError) { return(true); } if (settings1.UnwrapAreaError != settings2.UnwrapAreaError) { return(true); } if (settings1.UnwrapHardAngle != settings2.UnwrapHardAngle) { return(true); } if (settings1.UnwrapPackMargin != settings2.UnwrapPackMargin) { return(true); } } if (settings1.DefaultPhysicsMaterial != settings2.DefaultPhysicsMaterial) { return(true); } if (settings1.DefaultVisualMaterial != settings2.DefaultVisualMaterial) { return(true); } // Don't compare IsBuilt // No practical differences found return(false); }
internal static void BuildCollision(Transform meshGroupHolder, PolygonEntry[] polygonIndex, CSGBuildSettings buildSettings, List <Mesh> collisionMeshDictionary) { collisionMeshDictionary.Clear(); if (polygonIndex.Length > 0) { Mesh mesh = new Mesh(); List <Vector3> positionsList = new List <Vector3>(); List <Vector3> normalsList = new List <Vector3>(); List <Vector2> uvList = new List <Vector2>(); List <int> trianglesList = new List <int>(); for (int i = 0; i < polygonIndex.Length; i++) { if (polygonIndex[i] != null) { int positionOffset = positionsList.Count; int triangleOffset = trianglesList.Count; PolygonEntry polygonEntry = polygonIndex[i]; if (PolygonEntry.IsValid(polygonEntry) && polygonEntry.Positions.Length > 0 && !polygonEntry.ExcludeFromBuild) // Skip polygons that weren't built { if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) { FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); mesh = new Mesh(); positionsList.Clear(); normalsList.Clear(); uvList.Clear(); trianglesList.Clear(); positionOffset = 0; } positionsList.AddRange(polygonEntry.Positions); normalsList.AddRange(polygonEntry.Normals); uvList.AddRange(polygonEntry.UV); for (int j = 0; j < polygonEntry.Triangles.Length; j++) { trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); } polygonEntry.BuiltMesh = mesh; polygonEntry.BuiltVertexOffset = positionOffset; polygonEntry.BuiltTriangleOffset = triangleOffset; } } } FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); } }
internal static BuildStatus Build(List <Brush> brushes, CSGBuildSettings buildSettings, CSGBuildContext.BuildContext buildContext, Transform rootTransform, MaterialMeshDictionary materialMeshDictionary, List <Mesh> collisionMeshDictionary, bool polygonsRemoved, bool forceRebuild, Action <float> onProgressChange, Action <GameObject, Mesh> onFinalizeVisualMesh, Action <GameObject, Mesh> onFinalizeCollisionMesh, bool multithreaded) { CSGFactory.brushes = brushes; CSGFactory.buildSettings = buildSettings; CSGFactory.buildContext = buildContext; CSGFactory.rootTransform = rootTransform; CSGFactory.materialMeshDictionary = materialMeshDictionary; CSGFactory.collisionMeshDictionary = collisionMeshDictionary; CSGFactory.polygonsRemoved = polygonsRemoved; CSGFactory.forceRebuild = forceRebuild; CSGFactory.onProgressChange = onProgressChange; CSGFactory.onFinalizeVisualMesh = onFinalizeVisualMesh; CSGFactory.onFinalizeCollisionMesh = onFinalizeCollisionMesh; // DebugExclude.hackyHolder = DebugExclude.GetMetaDataHolder(); // BrushChunk.ResetNextID(); if (buildInProgress) { Debug.LogWarning("Existing build has not completed"); } if (multithreaded) { readyForFinalize = false; buildInProgress = true; ThreadPool.QueueUserWorkItem(CoreBuild, true); // FinalizeBuild is called by Tick when CoreBuild is complete return(BuildStatus.Started); } else { readyForFinalize = false; CoreBuild(null); bool buildOccurred = FinalizeBuild(); buildInProgress = false; return(buildOccurred ? BuildStatus.Complete : BuildStatus.Unnecessary); } }
internal static void FinalizeCollisionMesh(Transform meshGroupHolder, Mesh mesh, CSGBuildSettings buildSettings, List <Vector3> positionsList, List <Vector3> normalsList, List <Vector2> uvList, List <int> trianglesList, List <Mesh> collisionMeshDictionary) { Vector3[] positionsArray = new Vector3[positionsList.Count]; Vector3[] normalsArray = new Vector3[normalsList.Count]; Vector2[] uvArray = new Vector2[uvList.Count]; int[] trianglesArray = new int[trianglesList.Count]; positionsList.CopyTo(positionsArray); normalsList.CopyTo(normalsArray); uvList.CopyTo(uvArray); trianglesList.CopyTo(trianglesArray); mesh.vertices = positionsArray; mesh.normals = normalsArray; mesh.uv = uvArray; if (meshGroupHolder.position != Vector3.zero || meshGroupHolder.rotation != Quaternion.identity || meshGroupHolder.lossyScale != Vector3.one) { mesh.LocalizeToTransform(meshGroupHolder); } mesh.triangles = trianglesArray; GameObject newGameObject = new GameObject("CollisionMesh"); // Allow any editor dependent code to fire, e.g. assigning physics materials if (OnFinalizeCollisionMesh != null) { OnFinalizeCollisionMesh(newGameObject, mesh); } MeshCollider meshCollider = newGameObject.AddComponent <MeshCollider>(); meshCollider.sharedMesh = mesh; // Assign the physics material from build settings meshCollider.sharedMaterial = buildSettings.DefaultPhysicsMaterial; // Reparent newGameObject.transform.SetParent(meshGroupHolder, false); //.parent = meshGroupHolder; collisionMeshDictionary.Add(mesh); }
/// <summary> /// Builds the brushes into final meshes /// </summary> /// <param name="forceRebuild">If set to <c>true</c> all brushes will be built and cached data ignored, otherwise SabreCSG will only rebuild brushes it knows have changed</param> /// <param name="buildInBackground">If set to <c>true</c> the majority of the build will occur in a background thread</param> public virtual void Build(bool forceRebuild, bool buildInBackground) { // If any of the build settings have changed, force all brushes to rebuild if (!lastBuildSettings.IsBuilt || CSGBuildSettings.AreDifferent(buildSettings, lastBuildSettings)) { forceRebuild = true; } // Make sure we have the most accurate list of brushes, ignoring inactive objects brushes = new List <Brush>(transform.GetComponentsInChildren <Brush>(false)); // Let each brush know it's about to be built for (int i = 0; i < brushes.Count; i++) { brushes[i].PrepareToBuild(brushes, forceRebuild); } // Perform a check to make sure the default material is OK Material defaultMaterial = GetDefaultMaterial(); if (defaultMaterial == null) { Debug.LogError("Default fallback material file is missing, try reimporting SabreCSG"); } // Make sure the build context has been initialized. if (buildContext == null) { buildContext = BuildContext; } BuildStatus buildStatus = CSGFactory.Build(brushes, buildSettings, buildContext, this.transform, materialMeshDictionary, collisionMeshDictionary, polygonsRemoved, forceRebuild, OnBuildProgressChanged, OnFinalizeVisualMesh, OnFinalizeCollisionMesh, buildInBackground); if (buildStatus == BuildStatus.Complete) { OnBuildComplete(); } }
public virtual void OnBuildComplete() { polygonsRemoved = false; // Mark the brushes that have been built (so we can differentiate later if new brushes are built or not) builtBrushes.Clear(); builtBrushes.AddRange(brushes); // Copy the last build settings, so that we can make minor changes to built meshes that are consistent // with how they were built. E.g. maintaining tangents as appropriate lastBuildSettings = buildSettings.ShallowCopy(); lastBuildSettings.IsBuilt = true; // Make it clear that the lastBuildSettings refers to a completed build // Fire any post process build events FirePostBuildEvents(); }
internal static void BuildVisual(Transform meshGroupHolder, PolygonEntry[] polygonIndex, CSGBuildSettings buildSettings, CSGBuildContext.BuildContext buildContext, MaterialMeshDictionary materialMeshDictionary) { materialMeshDictionary.Clear(); // Reset statistics buildContext.buildMetrics.TotalMeshes = 0; buildContext.buildMetrics.TotalVertices = 0; buildContext.buildMetrics.TotalTriangles = 0; Dictionary <Material, List <PolygonEntry> > polygonMaterialTable = new Dictionary <Material, List <PolygonEntry> >(); for (int i = 0; i < polygonIndex.Length; i++) { PolygonEntry entry = polygonIndex[i]; if (PolygonEntry.IsValid(entry) && entry.Positions.Length > 0 && !entry.ExcludeFromBuild) // Skip polygons that weren't built { Material material = entry.Material; if (material == null) { material = buildSettings.DefaultVisualMaterial; } if (polygonMaterialTable.ContainsKey(material)) { polygonMaterialTable[material].Add(entry); } else { polygonMaterialTable.Add(material, new List <PolygonEntry>() { entry }); } } } foreach (KeyValuePair <Material, List <PolygonEntry> > row in polygonMaterialTable) { Mesh mesh = new Mesh(); List <Vector3> positionsList = new List <Vector3>(); List <Vector3> normalsList = new List <Vector3>(); List <Vector2> uvList = new List <Vector2>(); List <Color> colorsList = new List <Color>(); List <int> trianglesList = new List <int>(); for (int i = 0; i < row.Value.Count; i++) { int positionOffset = positionsList.Count; int triangleOffset = trianglesList.Count; PolygonEntry polygonEntry = row.Value[i]; if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) { FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); mesh = new Mesh(); positionsList.Clear(); normalsList.Clear(); uvList.Clear(); colorsList.Clear(); trianglesList.Clear(); positionOffset = 0; } positionsList.AddRange(polygonEntry.Positions); normalsList.AddRange(polygonEntry.Normals); uvList.AddRange(polygonEntry.UV); colorsList.AddRange(polygonEntry.Colors); for (int j = 0; j < polygonEntry.Triangles.Length; j++) { trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); } row.Value[i].BuiltMesh = mesh; row.Value[i].BuiltVertexOffset = positionOffset; row.Value[i].BuiltTriangleOffset = triangleOffset; } FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); } }
internal static void FinalizeCollisionMesh(Transform meshGroupHolder, Mesh mesh, CSGBuildSettings buildSettings, List <Vector3> positionsList, List <Vector3> normalsList, List <Vector2> uvList, List <int> trianglesList, List <Mesh> collisionMeshDictionary) { Vector3[] positionsArray = new Vector3[positionsList.Count]; Vector3[] normalsArray = new Vector3[normalsList.Count]; Vector2[] uvArray = new Vector2[uvList.Count]; int[] trianglesArray = new int[trianglesList.Count]; positionsList.CopyTo(positionsArray); normalsList.CopyTo(normalsArray); uvList.CopyTo(uvArray); trianglesList.CopyTo(trianglesArray); mesh.vertices = positionsArray; mesh.normals = normalsArray; mesh.uv = uvArray; if (meshGroupHolder.position != Vector3.zero || meshGroupHolder.rotation != Quaternion.identity || meshGroupHolder.lossyScale != Vector3.one) { mesh.LocalizeToTransform(meshGroupHolder); } mesh.triangles = trianglesArray; GameObject newGameObject = new GameObject("CollisionMesh"); // Allow any editor dependent code to fire, e.g. assigning physics materials if (OnFinalizeCollisionMesh != null) { OnFinalizeCollisionMesh(newGameObject, mesh); } MeshCollider meshCollider = newGameObject.AddComponent <MeshCollider>(); meshCollider.sharedMesh = mesh; // Assign the physics material from build settings meshCollider.sharedMaterial = buildSettings.DefaultPhysicsMaterial; // Reparent newGameObject.transform.SetParent(meshGroupHolder, false);//.parent = meshGroupHolder; #if UNITY_EDITOR if (buildSettings.SaveMeshesAsAssets) { // Make sure the folder exists to save into string path = SceneManager.GetActiveScene().path; path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)); if (!Directory.Exists(path)) { UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path)); } // Save to a file rather than leaving it as a scene asset UnityEditor.AssetDatabase.CreateAsset(mesh, Path.Combine(path, "CollisionMesh" + collisionMeshDictionary.Count + ".asset")); } #endif collisionMeshDictionary.Add(mesh); }
internal static void FinalizeVisualMesh(Transform meshGroupHolder, Mesh mesh, Material material, CSGBuildSettings buildSettings, CSGBuildContext.BuildContext buildContext, List <Vector3> positionsList, List <Vector3> normalsList, List <Vector2> uvList, List <Color> colorsList, List <int> trianglesList, MaterialMeshDictionary materialMeshDictionary) { Vector3[] positionsArray = new Vector3[positionsList.Count]; Vector3[] normalsArray = new Vector3[normalsList.Count]; Vector2[] uvArray = new Vector2[uvList.Count]; Color[] colorsArray = new Color[colorsList.Count]; int[] trianglesArray = new int[trianglesList.Count]; positionsList.CopyTo(positionsArray); normalsList.CopyTo(normalsArray); uvList.CopyTo(uvArray); trianglesList.CopyTo(trianglesArray); colorsList.CopyTo(colorsArray); mesh.vertices = positionsArray; mesh.normals = normalsArray; mesh.uv = uvArray; mesh.colors = colorsArray; if (meshGroupHolder.position != Vector3.zero || meshGroupHolder.rotation != Quaternion.identity || meshGroupHolder.lossyScale != Vector3.one) { mesh.LocalizeToTransform(meshGroupHolder); } mesh.triangles = trianglesArray; if (buildSettings.GenerateTangents) { // Generate tangents, necessary for some shaders mesh.GenerateTangents(); } buildContext.buildMetrics.TotalMeshes++; buildContext.buildMetrics.TotalVertices += positionsArray.Length; buildContext.buildMetrics.TotalTriangles += trianglesArray.Length / 3; GameObject newGameObject = new GameObject("MaterialMesh"); // Allow any editor dependent code to fire, e.g. lightmap unwrapping, static flags if (OnFinalizeVisualMesh != null) { OnFinalizeVisualMesh(newGameObject, mesh); } newGameObject.AddComponent <MeshFilter>().sharedMesh = mesh; MeshRenderer meshRenderer = newGameObject.AddComponent <MeshRenderer>(); meshRenderer.sharedMaterial = material; meshRenderer.shadowCastingMode = buildSettings.ShadowCastingMode; meshRenderer.reflectionProbeUsage = buildSettings.ReflectionProbeUsage; //newGameObject.transform.parent = meshGroupHolder; newGameObject.transform.SetParent(meshGroupHolder, false); #if UNITY_EDITOR if (buildSettings.SaveMeshesAsAssets) { // Make sure the folder exists to save into string path = SceneManager.GetActiveScene().path; path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)); if (!Directory.Exists(path)) { UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path)); } // Save to a file rather than leaving it as a scene asset UnityEditor.AssetDatabase.CreateAsset(mesh, Path.Combine(path, "VisualMesh" + materialMeshDictionary.MeshCount + ".asset")); } #endif materialMeshDictionary.Add(material, mesh, newGameObject); }
internal static void FinalizeVisualMesh(Transform meshGroupHolder, Mesh mesh, Material material, CSGBuildSettings buildSettings, CSGBuildContext.BuildContext buildContext, List <Vector3> positionsList, List <Vector3> normalsList, List <Vector2> uvList, List <Color> colorsList, List <int> trianglesList, MaterialMeshDictionary materialMeshDictionary) { Vector3[] positionsArray = new Vector3[positionsList.Count]; Vector3[] normalsArray = new Vector3[normalsList.Count]; Vector2[] uvArray = new Vector2[uvList.Count]; Color[] colorsArray = new Color[colorsList.Count]; int[] trianglesArray = new int[trianglesList.Count]; positionsList.CopyTo(positionsArray); normalsList.CopyTo(normalsArray); uvList.CopyTo(uvArray); trianglesList.CopyTo(trianglesArray); colorsList.CopyTo(colorsArray); mesh.vertices = positionsArray; mesh.normals = normalsArray; mesh.uv = uvArray; mesh.colors = colorsArray; if (meshGroupHolder.position != Vector3.zero || meshGroupHolder.rotation != Quaternion.identity || meshGroupHolder.lossyScale != Vector3.one) { mesh.LocalizeToTransform(meshGroupHolder); } mesh.triangles = trianglesArray; if (buildSettings.GenerateTangents) { // Generate tangents, necessary for some shaders mesh.GenerateTangents(); } buildContext.buildMetrics.TotalMeshes++; buildContext.buildMetrics.TotalVertices += positionsArray.Length; buildContext.buildMetrics.TotalTriangles += trianglesArray.Length / 3; GameObject newGameObject = new GameObject("MaterialMesh"); // Allow any editor dependent code to fire, e.g. lightmap unwrapping, static flags if (OnFinalizeVisualMesh != null) { OnFinalizeVisualMesh(newGameObject, mesh); } newGameObject.AddComponent <MeshFilter>().sharedMesh = mesh; newGameObject.AddComponent <MeshRenderer>().sharedMaterial = material; // newGameObject.transform.parent = meshGroupHolder; newGameObject.transform.SetParent(meshGroupHolder, false); materialMeshDictionary.Add(material, mesh, newGameObject); }