public void Mesh_CreateIntersectionBoxBrush_RetrieveMesh_RetrievedMeshIsEmpty() { var tree = CSGTree.Create( CreateBoxBrush(operation: CSGOperationType.Intersecting) ); GeneratedMeshContents generatedMesh = GeneratedMeshAndValidate(tree, simpleMeshTypes, expectEmpty: true); Assert.Null(generatedMesh); }
public void Mesh_CreateAdditiveBoxBrush_RetrieveMesh_RetrievedMeshIsACube() { var tree = CSGTree.Create( CreateBoxBrush(operation: CSGOperationType.Additive) ); GeneratedMeshContents generatedMesh = GeneratedMeshAndValidate(tree, simpleMeshTypes, expectEmpty: false); ValidateIsCorrectBox(generatedMesh); }
public void Mesh_CreateSubtractiveCubeBrush_RetrieveMesh_RetrievedMeshIsEmpty() { var tree = CSGTree.Create( CreateCubeBrush(operation: CSGOperationType.Subtractive) ); GeneratedMeshContents generatedMesh = GeneratedMeshAndValidate(tree, simpleMeshTypes, expectEmpty: true); Assert.Null(generatedMesh); }
public void Mesh_LargeAdditiveBoxBrushWithIntersectingBoxBrush_RetrievedMeshHasIntersectingBoxMaterial() { var tree = CSGTree.Create( CreateBoxBrush(operation: CSGOperationType.Additive, material: material2, size: Vector3.one * 2), CreateBoxBrush(operation: CSGOperationType.Intersecting, material: material1) ); GeneratedMeshContents generatedMesh = GeneratedMeshAndValidate(tree, materialMeshTypes, expectEmpty: false); ValidateIsCorrectBox(generatedMesh); Assert.AreEqual(materialID1, generatedMesh.description.surfaceParameter); }
public void Mesh_SubtractiveBoxBrushWithAdditiveBoxBrush_RetrievedMeshIsBox() { var tree = CSGTree.Create( CreateBoxBrush(operation: CSGOperationType.Subtractive, material: material2), CreateBoxBrush(operation: CSGOperationType.Additive, material: material1) ); GeneratedMeshContents generatedMesh = GeneratedMeshAndValidate(tree, materialMeshTypes, expectEmpty: false); ValidateIsCorrectBox(generatedMesh); Assert.AreEqual(materialID1, generatedMesh.description.surfaceParameter); }
public void Mesh_AdditiveCubeBrushOverlapsWithIntersectingCubeBrush_RetrievedMeshHasIntersectingCubeMaterial() { var tree = CSGTree.Create( CreateCubeBrush(operation: CSGOperationType.Additive, material: material2), CreateCubeBrush(operation: CSGOperationType.Intersecting, material: material1) ); GeneratedMeshContents generatedMesh = GeneratedMeshAndValidate(tree, materialMeshTypes, expectEmpty: false); ValidateIsCorrectCube(generatedMesh); Assert.AreEqual(materialID2, generatedMesh.description.surfaceParameter); }
static void ValidateIsCorrectBox(GeneratedMeshContents generatedMesh) { Assert.NotNull(generatedMesh.indices); Assert.NotNull(generatedMesh.positions); Assert.AreEqual(boxIndexCount, generatedMesh.indices.Length); // 6 sides, 2 triangles per side, 3 indices per triangle Assert.AreEqual(boxVertexCount, generatedMesh.positions.Length); // 6 sides, 4 vertices per side var vertexChannels = generatedMesh.description.meshQuery.UsedVertexChannels; // if ((vertexChannels & VertexChannelFlags.Color) == VertexChannelFlags.Color) // { // Assert.NotNull(generatedMesh.colors); // Assert.AreEqual(generatedMesh.positions.Length, generatedMesh.colors.Length); // } else // Assert.Null(generatedMesh.colors); if ((vertexChannels & VertexChannelFlags.Tangent) == VertexChannelFlags.Tangent) { Assert.NotNull(generatedMesh.tangents); Assert.AreEqual(generatedMesh.positions.Length, generatedMesh.tangents.Length); } else { Assert.Null(generatedMesh.tangents); } if ((vertexChannels & VertexChannelFlags.Normal) == VertexChannelFlags.Normal) { Assert.NotNull(generatedMesh.normals); Assert.AreEqual(generatedMesh.positions.Length, generatedMesh.normals.Length); } else { Assert.Null(generatedMesh.normals); } if ((vertexChannels & VertexChannelFlags.UV0) == VertexChannelFlags.UV0) { Assert.NotNull(generatedMesh.uv0); Assert.AreEqual(generatedMesh.positions.Length, generatedMesh.uv0.Length); } else { Assert.Null(generatedMesh.uv0); } }
static GeneratedMeshContents GeneratedMeshAndValidate(CSGTree tree, MeshQuery[] meshTypes, bool expectEmpty = false) { GeneratedMeshContents generatedMesh = null; GeneratedMeshDescription[] meshDescriptions = null; bool treeWasDirtyBefore = false; bool treeIsDirtyAfter = true; tree.SetDirty(); bool haveChanges = CSGManager.Flush(); // Note: optional if (haveChanges) { treeWasDirtyBefore = tree.Dirty; // Note: optional if (treeWasDirtyBefore) { meshDescriptions = tree.GetMeshDescriptions(meshTypes); if (meshDescriptions != null) { var meshDescription = meshDescriptions[0]; generatedMesh = tree.GetGeneratedMesh(meshDescription); } treeIsDirtyAfter = tree.Dirty; } } Assert.IsTrue(haveChanges); Assert.IsTrue(treeWasDirtyBefore); Assert.IsFalse(treeIsDirtyAfter); if (expectEmpty) { Assert.Null(meshDescriptions); Assert.Null(generatedMesh); } else { Assert.NotNull(meshDescriptions); Assert.NotNull(generatedMesh); Assert.AreEqual(meshDescriptions[0].meshQuery, meshTypes[0]); Assert.AreEqual(simpleMeshTypes.Length, meshDescriptions.Length); Assert.IsTrue(generatedMesh.description.vertexCount > 0 && generatedMesh.description.indexCount > 0); } return(generatedMesh); }
public static bool UpdateMesh(string baseName, GeneratedMeshContents generatedMesh, GeneratedMeshDescription inputMeshDescription, RenderSurfaceType renderSurfaceType, ref bool outputHasGeneratedNormals, ref Mesh sharedMesh, bool editorOnly) { var startUpdateMeshTime = EditorApplication.timeSinceStartup; { MeshInstanceManager.ClearOrCreateMesh(baseName, editorOnly, ref outputHasGeneratedNormals, ref sharedMesh); // finally, we start filling our (sub)meshes using the C# arrays sharedMesh.vertices = generatedMesh.positions; if (generatedMesh.normals != null) { sharedMesh.normals = generatedMesh.normals; } if (generatedMesh.tangents != null) { sharedMesh.tangents = generatedMesh.tangents; } // if (generatedMesh.colors != null) sharedMesh.colors = generatedMesh.colors; if (generatedMesh.uv0 != null) { sharedMesh.uv = generatedMesh.uv0; } // fill the mesh with the given indices sharedMesh.SetTriangles(generatedMesh.indices, 0, calculateBounds: true); //sharedMesh.RecalculateBounds(); //sharedMesh.bounds = generatedMesh.bounds; // why doesn't this work? } updateMeshTime += EditorApplication.timeSinceStartup - startUpdateMeshTime; if (renderSurfaceType != RenderSurfaceType.Normal) { outputHasGeneratedNormals = ((inputMeshDescription.meshQuery.UsedVertexChannels & VertexChannelFlags.Normal) != 0); } return(true); }
private static bool UpdateMesh(CSGModel model, GeneratedMeshDescription meshDescription, RenderSurfaceType renderSurfaceType, ref GeneratedMeshContents outputGeneratedMeshContents, ref bool outputHasGeneratedNormals, ref Mesh sharedMesh) { // create our arrays on the C# side with the correct size GeneratedMeshContents generatedMesh; var startGetModelMeshesTime = EditorApplication.timeSinceStartup; { generatedMesh = External.GetModelMesh(model.modelNodeID, meshDescription); if (generatedMesh == null) { return(false); } } getModelMeshesTime += EditorApplication.timeSinceStartup - startGetModelMeshesTime; UpdateMesh(generatedMesh, meshDescription, renderSurfaceType, ref outputHasGeneratedNormals, ref sharedMesh); /* #if UNITY_2018_3_OR_NEWER * var modelGameObject = model.gameObject; * var prefabStage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetPrefabStage(modelGameObject); * if (prefabStage != null && string.IsNullOrEmpty(AssetDatabase.GetAssetPath(sharedMesh))) * { * var prefabAssetPath = prefabStage.prefabAssetPath; * if (!string.IsNullOrEmpty(prefabAssetPath)) * AssetDatabase.AddObjectToAsset(sharedMesh, prefabAssetPath); * UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(modelGameObject.scene); * } #endif */ outputGeneratedMeshContents = generatedMesh; return(true); }
public void GenerateMesh() { // The mesh queries define how meshes are generated using the BrushMesh surface layer information MeshQuery[] meshQueries = GetMeshQueries(); // Create a tree // Note: this can be cached and updated separately. CSGTree tree = CreateTree(); // At this point, when you have a tree that you update frequently, you can also potentially check if it's Dirty or not. // If it is, it means it has been modified since the last time tree.GetMeshDescriptions has been called. // Find all the potential meshes that could be generated with our queries. // There is no guarantee that these meshes will exist. It might also be split into multiple meshes when it's too large GeneratedMeshDescription[] meshDescriptions = tree.GetMeshDescriptions(meshQueries, VertexChannelFlags.All); if (meshDescriptions != null) { // Iterate through all the meshes that we've found foreach (var meshDescription in meshDescriptions) { // At this point, it's possible to check the Hash values stored in meshDescription, // to check if this particular type of mesh has already been generated before (perhaps in a previous iteration) // You could then potentially early out and avoid retrieving the generated mesh and converting it into a new UnityEngine.Mesh. // Actually generate the mesh on the native side, potentially generate smooth normals etc. and retrieve the mesh. GeneratedMeshContents contents = tree.GetGeneratedMesh(meshDescription); // Note: It's possible to re-use this UnityEngine.Mesh UnityEngine.Mesh unityMesh = new UnityEngine.Mesh(); // Copy the generated mesh to the UnityEngine.Mesh contents.CopyTo(unityMesh); // ... assign it to a MeshRenderer/MeshCollider etc. } } }