I want to cut meshes by plane.
try this code, but get only one of parts of cutting mesh.
Screenshot 1
Screenshot 2
public void SliceIt()
{
Vector3[] vertices = mesh.vertices;
Transform clone = clone = ((Transform)Instantiate(transform, transform.position + new Vector3(0, 0.25f, 0), transform.rotation));
Mesh meshSlice = clone.GetComponent<MeshFilter>().sharedMesh;
Vector3[] verticesSlice = meshSlice.vertices;
List<Vector3> verticesSlice2 = new List<Vector3>();
Mesh cutplanemesh = cutplane.GetComponent<MeshFilter>().sharedMesh;
Vector3[] cutplanevertices = cutplanemesh.vertices;
p1 = cutplane.TransformPoint(cutplanevertices[40]);
p2 = cutplane.TransformPoint(cutplanevertices[20]);
p3 = cutplane.TransformPoint(cutplanevertices[0]);
var myplane = new Plane(p1, p2, p3);
for (var i = 0; i < vertices.Length; i++)
{
var tmpverts = transform.TransformPoint(vertices[i]); // original object vertices
if (myplane.GetSide(tmpverts))
{
vertices[i] = transform.InverseTransformPoint(new Vector3(tmpverts.x, tmpverts.y - (myplane.GetDistanceToPoint(tmpverts)), tmpverts.z));
verticesSlice[i] = transform.InverseTransformPoint(new Vector3(tmpverts.x, tmpverts.y, tmpverts.z));
var v = transform.InverseTransformPoint(new Vector3(tmpverts.x, tmpverts.y, tmpverts.z));
verticesSlice2.Add(v);
}
else
{
var v = transform.InverseTransformPoint(new Vector3(tmpverts.x, tmpverts.y - (myplane.GetDistanceToPoint(tmpverts)), tmpverts.z));
verticesSlice2.Add(v);
}
}
mesh.vertices = verticesSlice;
mesh.RecalculateBounds();
meshSlice.vertices = verticesSlice2.ToArray();
meshSlice.RecalculateBounds();
}
I got this code from here.
I also read this question, but I couldn't figure how to split triangles which belong to positive and negative sides of plane.
You can use already existing assets on asset store:
https://www.assetstore.unity3d.com/#!/content/59618
It is not only cut a mesh, it also cut colliders, do some optimizations and it is extandable.
Related
I would like to see an example on how to proceduraly extrude a face of a mesh,parts of a mesh face (if it's tesselated) using Unity Meshes API and C# Scripting.
private static Mesh Testp(Mesh mesh)
{
List<Vector3> vertices = new List<Vector3>(mesh.vertices.ToArray());
List<Vector3> normals = new List<Vector3>(mesh.normals.ToArray());
List<int> triangles = new List<int>(mesh.triangles);
for (var i = 0; i < mesh.triangles.Length / 3; i += 3)
{
var faceNormal = CalculateFaceNormal(normals, triangles, i);
if (Math.Abs(Vector3.Dot(Vector3.forward, faceNormal) - (-1f)) < 0.0001f)
{
triangles.Add(vertices.Count);
triangles.Add(vertices.Count+1);
triangles.Add(vertices.Count+2);
vertices.Add(vertices[triangles[i]] + faceNormal * 1f);
vertices.Add(vertices[triangles[i+1]] + faceNormal * 1f);
vertices.Add(vertices[triangles[i+2]] + faceNormal * 1f);
normals.Add(faceNormal);
normals.Add(faceNormal);
normals.Add(faceNormal);
}
}
mesh.Clear();
mesh.vertices = vertices.ToArray();
mesh.normals = normals.ToArray();
mesh.triangles = triangles.ToArray();
return mesh;
}
this is what I've tried so far, but this doesnt really extrude a face, it just creates a new face offseted by a value.
I'm learning about mesh rendering in unity.
I followed the documentation and was able to draw a blue quad on the screen.
However I can't figure out how render thinng such that the entire monitor is used.
I think what I'm missing is setting some sort of projections for this coordinate system to match. But how can I do that? Ideally I would like to do:
x_start = 0;
x_end = W;
y_start = 0;
y_end = H;
And have this quad cover the entire screen.
Here is the code:
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class MeshRendererTest : MonoBehaviour
{
// Start is called before the first frame update
static Material mat;
float z;
float W;
float H;
Matrix4x4 projectionMatrix;
void Start(){
W = 2480.0f;
H = 2416.0f;
//projectionMatrix = Matrix4x4.Ortho(0, (int)W, (int)H, 0, -1, 100);
Shader shader = Shader.Find("Hidden/Internal-Colored");
//Shader shader2 = Shader.Find("Standard");
//Shader shader2 = Shader.Find("Unlit/UnlitAlphaWithFade");
mat = new Material(shader);
mat.hideFlags = HideFlags.HideAndDontSave;
//texMat.hideFlags = HideFlags.HideAndDontSave;
// Turn backface culling off
mat.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
//texMat.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
// Turn off depth writes
mat.SetInt("_ZWrite", 0);
//texMat.SetInt("_ZWrite", 0);
z = 999.0f;
mat.SetColor("_Color", Color.blue);
mat.SetPass(0);
MeshRenderer meshRenderer = gameObject.AddComponent<MeshRenderer>();
if (meshRenderer == null){
Debug.Log("Mesh Renderer is NUll");
return;
}
//meshRenderer.sharedMaterial = new Material(Shader.Find("Standard"));
meshRenderer.sharedMaterial = mat;
MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
if (meshFilter == null){
Debug.Log("Mesh Filter is NUll");
return;
}
Mesh mesh = new Mesh();
float x_start = 0;
float x_end = x_start + 100;
float y_start = 0;
float y_end = y_start + 600;
Vector3[] vertices = new Vector3[4]
{
new Vector3(x_start, y_start, z),
new Vector3(x_end, y_start, z),
new Vector3(x_start, y_end, z),
new Vector3(x_end, y_end, z)
};
mesh.vertices = vertices;
int[] tris = new int[6]
{
// lower left triangle
0, 2, 1,
// upper right triangle
2, 3, 1
};
mesh.triangles = tris;
Vector3[] normals = new Vector3[4]
{
-Vector3.forward,
-Vector3.forward,
-Vector3.forward,
-Vector3.forward
};
mesh.normals = normals;
Vector2[] uv = new Vector2[4]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(1, 1)
};
mesh.uv = uv;
meshFilter.mesh = mesh;
}
void OnRenderObject(){
TestStuff();
}
void TestStuff(){
Camera.main.ResetProjectionMatrix();
Matrix4x4 newProj = Matrix4x4.identity;
newProj = newProj * transform.localToWorldMatrix;
newProj = newProj * Camera.main.projectionMatrix;
newProj = newProj * projectionMatrix;
Camera.main.projectionMatrix = newProj;
}
}
Good afternoon! I'm trying to sew up two meshes. I do this as follows: first I convert the sprite into a mesh, then I duplicate the resulting mesh, shift it along the "z" axis, invert it, and then sew it up. But I faced such a problem: he sews rectangular meshes well, but in circular meshes there are some defects on the sides. So, how can you sew up these sides? (Materials and code)
public class ConvertSpriteInMesh : MonoBehaviour
{
public Sprite sprite;
private MeshDraft meshDraft = new MeshDraft();
private Mesh mesh;
void Start()
{
GetComponent<MeshFilter>().mesh = SpriteToMesh(sprite);
SewingUp();
}
/// <summary>
/// Sewing up nets
/// </summary>
private void SewingUp()
{
mesh = GetComponent<MeshFilter>().mesh;
meshDraft = new MeshDraft(mesh);
int leftVertical = mesh.vertices.Length / 2; // getting the beginning of the left vertical of the mesh
int index = mesh.vertices.Length;
for (int i = 0; i < leftVertical - 1; i++)
{
meshDraft.AddQuad(mesh.vertices[i], mesh.vertices[i+1], mesh.vertices[i + leftVertical + 1],mesh.vertices[i+leftVertical],
index);
index += 4;
}
GetComponent<MeshFilter>().mesh = meshDraft.ToMesh(); // assign the resulting mesh
}
/// <summary>
/// Convert Sprite to Mesh
/// </summary>
/// <param name="_sprite"></param>
/// <returns></returns>
private Mesh SpriteToMesh(Sprite _sprite)
{
// declaring variables
Mesh mesh = new Mesh();
Vector3[] _verticles;
int[] _triangle;
// assigning values
_verticles = Array.ConvertAll(_sprite.vertices, i => (Vector3)i);
_triangle = Array.ConvertAll(_sprite.triangles, i => (int)i);
// changing the size of the array
Array.Resize(ref _verticles, _verticles.Length * 2);
Array.Resize(ref _triangle, _triangle.Length * 2);
// adding another side
for (int i = 0; i < _verticles.Length / 2; i++)
{
_verticles[_verticles.Length / 2 + i] = new Vector3(_verticles[i].x, _verticles[i].y, 0.5f);
}
for (int i = 0; i < _triangle.Length / 2; i++)
{
_triangle[_triangle.Length / 2 + i] = _triangle[i] + (_verticles.Length / 2);
}
// invert the second side
for(int i = _triangle.Length / 2; i < _triangle.Length; i += 3) {
var temp = _triangle[i];
_triangle[i] = _triangle[i + 1];
_triangle[i + 1] = temp;
}
// assigning the mesh
mesh.vertices = _verticles;
mesh.triangles = _triangle;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
return mesh;
}
}
public partial class MeshDraft {
public string name = "";
public List<Vector3> vertices = new List<Vector3>();
public List<int> triangles = new List<int>();
public List<Vector3> normals = new List<Vector3>();
public List<Vector4> tangents = new List<Vector4>();
public List<Vector2> uv = new List<Vector2>();
public List<Vector2> uv2 = new List<Vector2>();
public List<Vector2> uv3 = new List<Vector2>();
public List<Vector2> uv4 = new List<Vector2>();
public List<Color> colors = new List<Color>();
public MeshDraft(Mesh mesh) {
name = mesh.name;
vertices.AddRange(mesh.vertices);
triangles.AddRange(mesh.triangles);
normals.AddRange(mesh.normals);
tangents.AddRange(mesh.tangents);
uv.AddRange(mesh.uv);
uv2.AddRange(mesh.uv2);
uv3.AddRange(mesh.uv3);
uv4.AddRange(mesh.uv4);
colors.AddRange(mesh.colors);
}
public void AddQuad(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3, int index, Color color = default(Color)) {
vertices.Add(v0);
vertices.Add(v1);
vertices.Add(v2);
vertices.Add(v3);
Vector3 normal0 = Vector3.Cross(v2 - v1, v3 - v1).normalized;
Vector3 normal1 = Vector3.Cross(v1 - v0, v2 - v0).normalized;
normals.Add(normal0);
normals.Add(normal0);
normals.Add(normal1);
normals.Add(normal1);
colors.Add(color);
colors.Add(color);
colors.Add(color);
colors.Add(color);
triangles.Add(index);
triangles.Add(index + 1);
triangles.Add(index + 2);
triangles.Add(index);
triangles.Add(index + 2);
triangles.Add(index + 3);
}
public Mesh ToMesh() {
var mesh = new Mesh { name = name };
mesh.SetVertices(vertices);
mesh.SetTriangles(triangles, 0);
mesh.SetNormals(normals);
mesh.SetTangents(tangents);
mesh.SetUVs(0, uv);
mesh.SetUVs(1, uv2);
mesh.SetUVs(2, uv3);
mesh.SetUVs(3, uv4);
mesh.SetColors(colors);
return mesh;
}
Successful stitching (screen)
Bad stitching (screen)
I was given an answer on another forum, who is interested, I will leave a link here - https://www.cyberforum.ru/unity/thread2823987.html
Yesterday others on Stack Overflow helped me determine how to recolor a mesh triangle to red by clicking on it, it works great, the only problem is that the 3 vertices that get recolored are shared between triangles. This results in coloration that looks rather smeared. I'm really hoping there's a way to color only a single face (or normal if you will).
I've attached the following script to my mesh that uses a raycast to determine the surface coordinate and translate a green cube there. The gif below will better illustrate this problem.
Once again, any help or insight into this would be greatly appreciated. Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyRayDraw : MonoBehaviour
{
public GameObject cube;
private MeshRenderer meshRenderer;
Mesh mesh;
Vector3[] vertices;
Color[] colorArray;
private void Start()
{
mesh = transform.GetComponent<MeshFilter>().mesh;
vertices = mesh.vertices;
colorArray = new Color[vertices.Length];
for (int k = 0; k < vertices.Length; k++)
{
colorArray[k] = Color.white;
}
mesh.colors = colorArray;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
Snap(hit.point); // Moves the green cube
int[] triangles = mesh.triangles;
var vertIndex1 = triangles[hit.triangleIndex * 3 + 0];
var vertIndex2 = triangles[hit.triangleIndex * 3 + 1];
var vertIndex3 = triangles[hit.triangleIndex * 3 + 2];
colorArray[vertIndex1] = Color.red;
colorArray[vertIndex2] = Color.red;
colorArray[vertIndex3] = Color.red;
mesh.colors = colorArray;
}
else
{
Debug.Log("no hit");
}
}
}
}
As you say the issue is that the vertices are shared between triangles but coloring is always vertex based.
The idea for a solution is:
for each vertex of the hit triangle check if it is used by other triangles
if so copy its position to create a new separated vertex
update the triangle to use the newly created vertex indices
(evtl.) use RecalculateNormals to make the triangles face outside without having to care about the order of provided vertices
using System.Linq;
using UnityEngine;
public class MyRayDraw : MonoBehaviour
{
public GameObject cube;
// Better to reference those already in the Inspector
[SerializeField] private MeshFilter meshFilter;
[SerializeField] private MeshRenderer meshRenderer;
[SerializeField] private MeshCollider meshCollider;
private Mesh _mesh;
private void Awake()
{
if (!meshFilter) meshFilter = GetComponent<MeshFilter>();
if (!meshRenderer) meshRenderer = GetComponent<MeshRenderer>();
if (!meshCollider) meshCollider = GetComponent<MeshCollider>();
_mesh = meshFilter.mesh;
// create new colors array where the colors will be created
var colors = new Color[_mesh.vertices.Length];
for (var k = 0; k < colors.Length; k++)
{
colors[k] = Color.white;
}
_mesh.colors = colors;
}
private void Update()
{
if (!Input.GetMouseButtonDown(0)) return;
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out var hit))
{
Debug.Log(hit.triangleIndex);
//cube.transform.position = hit.point;
// Get current vertices, triangles and colors
var vertices = _mesh.vertices;
var triangles = _mesh.triangles;
var colors = _mesh.colors;
// Get the vert indices for this triangle
var vert1Index = triangles[hit.triangleIndex * 3 + 0];
var vert2Index = triangles[hit.triangleIndex * 3 + 1];
var vert3Index = triangles[hit.triangleIndex * 3 + 2];
// Get the positions for the vertices
var vert1Pos = vertices[vert1Index];
var vert2Pos = vertices[vert2Index];
var vert3Pos = vertices[vert3Index];
// Now for all three vertices we first check if any other triangle if using it
// by simply count how often the indices are used in the triangles list
var vert1Occurrences = 0;
var vert2Occurrences = 0;
var vert3Occurrences = 0;
foreach (var index in triangles)
{
if (index == vert1Index) vert1Occurrences++;
else if (index == vert2Index) vert2Occurrences++;
else if (index == vert3Index) vert3Occurrences++;
}
// Create copied Lists so we can dynamically add entries
var newVertices = vertices.ToList();
var newColors = colors.ToList();
// Now if a vertex is shared we need to add a new individual vertex
// and also an according entry for the color array
// and update the vertex index
// otherwise we will simply use the vertex we already have
if (vert1Occurrences > 1)
{
newVertices.Add(vert1Pos);
newColors.Add(new Color());
vert1Index = newVertices.Count - 1;
}
if (vert2Occurrences > 1)
{
newVertices.Add(vert2Pos);
newColors.Add(new Color());
vert2Index = newVertices.Count - 1;
}
if (vert3Occurrences > 1)
{
newVertices.Add(vert3Pos);
newColors.Add(new Color());
vert3Index = newVertices.Count - 1;
}
// Update the indices of the hit triangle to use the (eventually) new
// vertices instead
triangles[hit.triangleIndex * 3 + 0] = vert1Index;
triangles[hit.triangleIndex * 3 + 1] = vert2Index;
triangles[hit.triangleIndex * 3 + 2] = vert3Index;
// color these vertices
newColors[vert1Index] = Color.red;
newColors[vert2Index] = Color.red;
newColors[vert3Index] = Color.red;
// write everything back
_mesh.vertices = newVertices.ToArray();
_mesh.triangles = triangles;
_mesh.colors = newColors.ToArray();
_mesh.RecalculateNormals();
}
else
{
Debug.Log("no hit");
}
}
}
Note, however, that this works with simple coloring but might not for complex textures with UV mapping. You would have to also update the mesh.uv if using UV mapped textures.
I have this code (not originally my invention) that is working fine, but I have hit upon a problem with it. I can't seem to render the second submesh, with another material.
I can't figure out the problem lies in [renderer] part or the 2nd "submesh" that is perhaps never combining.
#script RequireComponent(MeshFilter)
#script RequireComponent(MeshRenderer)
private var matarray : Material[] = new Material[2];
var mat : Material;
var mat2 : Material;
function Start () {
matarray[0] = mat;
matarray[1] = mat2;
for (var child in transform)
child.position += transform.position;
transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
var meshFilters = GetComponentsInChildren(MeshFilter);
var combine : CombineInstance[] = new CombineInstance[meshFilters.Length-1];
var index = 0;
for (var i = 0; i < meshFilters.Length; i++) {
if (meshFilters[i].sharedMesh == null)
continue;
combine[index].mesh = meshFilters[i].sharedMesh;
combine[index++].transform = meshFilters[i].transform.localToWorldMatrix;
renderer.sharedMaterials = matarray;
meshFilters[i].active = false;
}
GetComponent(MeshFilter).mesh = new Mesh();
GetComponent(MeshFilter).mesh.CombineMeshes(combine);
renderer.active = true;
//here seems to be the trouble.
renderer.sharedMaterials = matarray;
}
Alternatively I have tried,
renderer.sharedMaterials[1] = matarray[1];
In short, I'm in trouble. I have to save few draw calls, which is not looking good unless this gets fixed.