How to create a mesh dynamically In unity - c#

I have vertex that have a color value.
I'd like to make a mesh using vertex with the same color values.
This picture is an example.
I took pictures with my Android Phone, and I did image segmentation on the object
So I got a color value corresponding to the coordinate value.
I succeeded in just making textures. please check the image.
But I want a mesh object.
Below is making texture code.
var pixel = await this.segmentation.SegmentAsync(rotated, scaled.width, scaled.height);
// int pixel[][]; // image segmentation using tensorflow
Color transparentColor = new Color32(255, 255, 255, 0); // transparent
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
int class_output = pixel[y][x];
texture.SetPixel(x, y, pixel[y][x] == 0 ? transparentColor : colors[class_output]);
}
}
texture.Apply();
How can I make a mesh object?

1- Set a prefab with a MeshFilter and a MeshRenderer.
2- Variables inside the script that you will need to fill.
// This first list contains every vertex of the mesh that we are going to render
public List<Vector3> newVertices = new List<Vector3>();
// The triangles tell Unity how to build each section of the mesh joining
// the vertices
public List<int> newTriangles = new List<int>();
// The UV list is unimportant right now but it tells Unity how the texture is
// aligned on each polygon
public List<Vector2> newUV = new List<Vector2>();
// A mesh is made up of the vertices, triangles and UVs we are going to define,
// after we make them up we'll save them as this mesh
private Mesh mesh;
3- Initialize the mesh
void Start () {
mesh = GetComponent<MeshFilter> ().mesh;
float x = transform.position.x;
float y = transform.position.y;
float z = transform.position.z;
newVertices.Add( new Vector3 (x , y , z ));
newVertices.Add( new Vector3 (x + 1 , y , z ));
newVertices.Add( new Vector3 (x + 1 , y-1 , z ));
newVertices.Add( new Vector3 (x , y-1 , z ));
newTriangles.Add(0);
newTriangles.Add(1);
newTriangles.Add(3);
newTriangles.Add(1);
newTriangles.Add(2);
newTriangles.Add(3);
newUV.Add(new Vector2 (tUnit * tStone.x, tUnit * tStone.y + tUnit));
newUV.Add(new Vector2 (tUnit * tStone.x + tUnit, tUnit * tStone.y + tUnit));
newUV.Add(new Vector2 (tUnit * tStone.x + tUnit, tUnit * tStone.y));
newUV.Add(new Vector2 (tUnit * tStone.x, tUnit * tStone.y));
mesh.Clear ();
mesh.vertices = newVertices.ToArray();
mesh.triangles = newTriangles.ToArray();
mesh.uv = newUV.ToArray(); // add this line to the code here
mesh.Optimize ();
mesh.RecalculateNormals ();
}
This code will draw a square at the position of the prefab, if you keep adding vertices you can generate a more complex mesh.
The source of information is a tutorial to generate mensh for a terrain like minecrat, check the link for more information.

The answer which has been selected best is, in my opinion, faulty for four reasons. First, it is deprecated. Second, it is more complex than necessary. Third, it offers little explanation, and finally, it is mostly just a copy from someone else's blog post. For that reason, I offer a new suggestion. For more info, view the documentation here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class meshmaker : MonoBehaviour {
Mesh mesh;
MeshFilter meshFilter;
Vector3[] newVertices;
int[] newTriangles;
// Use this for initialization
void Start () {
//First, we create an array of vector3's. Each vector3 will
//represent one vertex in our mesh. Our shape will be a half
//cube (probably the simplest 3D shape we can make.
newVertices = new Vector3[4];
newVertices [0] = new Vector3 (0, 0, 0);
newVertices [1] = new Vector3 (1, 0, 0);
newVertices [2] = new Vector3 (0, 1, 0);
newVertices [3] = new Vector3 (0, 0, 1);
//Next, we create an array of integers which will represent
//triangles. Triangles are built by taking integers in groups of
//three, with each integer representing a vertex from our array of
//vertices. Note that the integers are in a certain order. The order
//of integers determines the normal of the triangle. In this case,
//connecting 021 faces the triangle out, while 012 faces the
//triangle in.
newTriangles = new int[12];
newTriangles[0] = 0;
newTriangles[1] = 2;
newTriangles[2] = 1;
newTriangles[3] = 0;
newTriangles[4] = 1;
newTriangles[5] = 3;
newTriangles[6] = 0;
newTriangles[7] = 3;
newTriangles[8] = 2;
newTriangles[9] = 1;
newTriangles[10] = 2;
newTriangles[11] = 3;
//We instantiate our mesh object and attach it to our mesh filter
mesh = new Mesh ();
meshFilter = gameObject.GetComponent<MeshFilter> ();
meshFilter.mesh = mesh;
//We assign our vertices and triangles to the mesh.
mesh.vertices = newVertices;
mesh.triangles = newTriangles;
}
Ta da! Your very own half-cube.

Related

Unity - Project a texture on a mesh using C# (No Shaders)

I'm trying to project a texture on a simple cube meshFilter using only C# but I'm having a bit of a hard time understanding what to do. I almost got it working for the X axis rotation and there is a lot of bad warping for Y/Z. Basically, I update the UVs when the position/rotation of the camera changes, here is my code :
[ExecuteInEditMode]
public class ObjectEditor : MonoBehaviour {
public GameObject Model;
public void UpdateTexture(Camera camera) {
MeshFilter[] mesheFilters = Model.GetComponentsInChildren<MeshFilter>();
foreach (MeshFilter meshFilter in mesheFilters) {
int size = meshFilter.sharedMesh.vertices.Length;
Vector2[] uvs = new Vector2[size];
for (int i = 0; i < size; i++) {
uvs[i] = vertexToUVPosition(camera, meshFilter, i);
}
meshFilter.sharedMesh.SetUVs(0, uvs);
}
}
private Vector2 vertexToUVPosition(Camera camera, MeshFilter meshFilter, int index) {
Vector3 vertex = meshFilter.sharedMesh.vertices[index];
Matrix4x4 VP = camera.projectionMatrix * camera.worldToCameraMatrix;
Vector4 worldPos = new Vector4(vertex.x, vertex.y, vertex.z, 1f);
Vector4 clipPos = VP * worldPos;
clipPos *= 1f / clipPos.w;
return camera.WorldToScreenPoint(clipPos);
}
}
Everything regarding the projection happens in vertexToUVPosition.
And here is what I have right now (the projected texture is a simple black/white checkerboard) :
Can someone experienced in projections explain to me what I'm doing wrong and maybe provide a sample C# code that works correctly? Thank you.
I found a solution which solves the problem completely but I guess it is not mathematically correct since I don't really know much about matrixes and projections. It is however a good starting point for whoever wants to do something similar without any experience.
Before showing the code, some things you should setup in order to make it easier to debug potential problems :
Make sure your texture is in clamp mode, it will be easier to see if the projection works correctly.
Position your object at (0,0,0).
Position your camera at (0,0,0) and make sure it is in perspective mode.
Use another camera in orthographic mode to look at the projected texture and validate it is shown correctly.
The algorithm :
// Calculate the VP matrix based on the Perspective Camera
VP = camera.projectionMatrix * camera.worldToCameraMatrix
foreach vertex in mesh
// Replace the "w" component by 1.
worldPosition = new Vector4(vertex.x, vertex.y, vertex.z, 1f);
clipPosition = VP * worldPosition;
// Small correction on Y axis (maybe someone can explain why I need this?).
clipPosition.Scale(new Vector3(1, 0.5f, 1));
// Use the clipPosition as UV coordinates for that vertex.
...
My implementation :
[ExecuteInEditMode]
public class ObjectEditor : MonoBehaviour {
public GameObject Model;
public void UpdateTexture(Camera camera) {
Matrix4x4 vp = camera.projectionMatrix * camera.worldToCameraMatrix;
MeshFilter[] mesheFilters = Model.GetComponentsInChildren<MeshFilter>();
foreach (MeshFilter meshFilter in mesheFilters) {
int size = meshFilter.sharedMesh.vertices.Length;
Vector2[] uvs = new Vector2[size];
for (int i = 0; i < size; i++) {
uvs[i] = vertexToUVPosition(vp, meshFilter, i);
}
meshFilter.sharedMesh.SetUVs(0, uvs);
}
}
private Vector2 vertexToUVPosition(Matrix4x4 vp, MeshFilter meshFilter, int index) {
Vector3 vertex = meshFilter.sharedMesh.vertices[index];
Vector4 worldPos = new Vector4(vertex.x, vertex.y, vertex.z, 1f);
Vector4 clipPos = vp * worldPos;
clipPos.Scale(new Vector3(1, 0.5f, 1));
return clipPos;
}
}
The result - Test with rotation on two axis (40,-45,0):
The orthographic camera view :

Triangle.NET - How to add vertex to existing triangulation?

I've looked through what seems like every question and resource there is for Triangle.NET trying to find an answer to how to insert a vertex into an existing triangulation. The closest I've gotten was in the discussion archives for Traingle.Net where someone asked a similar question (discussion id 632458) but unfortunately, the answer was not what I was looking for.
My goal here is to make a destructible wall in Unity where, when the player shoots the wall, it will create a hole in the wall (like in Rainbow Six Siege).
Here's what I did for my original implementation:
Create initial triangulation using the four corners of the wall.
When the player shoots, perform a raycast, if the raycast intersects with the wall then add the point of intersection to the polygon variable and re-triangulate the entire mesh using that variable.
Draw new triangulation on the wall as a texture to visualise what's happening.
Repeat.
As you can see, step 2 is the problem.
Because I re-triangulate the entire mesh every time the player hits the wall, the more times the player hits the wall the slower the triangulation gets as the number of vertices rises. This could be fine I guess, but I want destructible walls to play a major role in my game so this is not acceptable.
So, digging through the Triangle.Net source code, I find an internal method called InsertVertex. The summary for this method states:
Insert a vertex into a Delaunay triangulation, performing flips as necessary to maintain the Delaunay property.
This would mean I wouldn't have to re-triangulate every time the player shoots!
So I get to implementing this method, and...it doesn't work. I get an error like the one below:
NullReferenceException: Object reference not set to an instance of an object
TriangleNet.TriangleLocator.PreciseLocate (TriangleNet.Geometry.Point searchpoint, TriangleNet.Topology.Otri& searchtri, System.Boolean stopatsubsegment) (at Assets/Triangle.NET/TriangleLocator.cs:146)
I have been stuck on this problem for days and I cannot solve it for the life of me! If anyone who is knowledgeable enough with the Triangle.NET library would be willing to help me I would be so grateful! Along with that, if there is a better alternative to either the implementation or library I'm using (for my purpose which I outlined above) that would also be awesome!
Currently, how I've set up the scene is really simple, I just have a quad which I scaled up and added the script below to it as a component. I then linked that component to a shoot raycast script attached to the Main Camera:
How the scene is setup.
What it looks like in Play Mode.
The exact Triangle.Net repo I cloned is this one.
My code is posted below:
using UnityEngine;
using TriangleNet.Geometry;
using TriangleNet.Topology;
using TriangleNet.Meshing;
public class Delaunay : MonoBehaviour
{
[SerializeField]
private int randomPoints = 150;
[SerializeField]
private int width = 512;
[SerializeField]
private int height = 512;
private TriangleNet.Mesh mesh;
Polygon polygon = new Polygon();
Otri otri = default(Otri);
Osub osub = default(Osub);
ConstraintOptions constraintOptions = new ConstraintOptions() { ConformingDelaunay = true };
QualityOptions qualityOptions = new QualityOptions() { MinimumAngle = 25 };
void Start()
{
osub.seg = null;
Mesh objMesh = GetComponent<MeshFilter>().mesh;
// Add four corners of wall (quad in this case) to polygon.
//foreach (Vector3 vert in objMesh.vertices)
//{
// Vector2 temp = new Vector2();
// temp.x = map(vert.x, -0.5f, 0.5f, 0, 512);
// temp.y = map(vert.y, -0.5f, 0.5f, 0, 512);
// polygon.Add(new Vertex(temp.x, temp.y));
//}
// Generate random points and add to polygon.
for (int i = 0; i < randomPoints; i++)
{
polygon.Add(new Vertex(Random.Range(0.0f, width), Random.Range(0.0f, height)));
}
// Triangulate polygon.
delaunayTriangulation();
}
// When left click is pressed, a raycast is sent out. If that raycast hits the wall, updatePoints() is called and is passed in the location of the hit (hit.point).
public void updatePoints(Vector3 pos)
{
// Convert pos to local coords of wall.
pos = transform.InverseTransformPoint(pos);
Vertex newVert = new Vertex(pos.x, pos.y);
//// Give new vertex a unique id.
//if (mesh != null)
//{
// newVert.id = mesh.NumberOfInputPoints;
//}
// Insert new vertex into existing triangulation.
otri.tri = mesh.dummytri;
mesh.InsertVertex(newVert, ref otri, ref osub, false, false);
// Draw result as a texture onto the wall so to visualise what is happening.
draw();
}
private void delaunayTriangulation()
{
mesh = (TriangleNet.Mesh)polygon.Triangulate(constraintOptions, qualityOptions);
draw();
}
void draw()
{
Texture2D tx = new Texture2D(width, height);
// Draw triangulation.
if (mesh.Edges != null)
{
foreach (Edge edge in mesh.Edges)
{
Vertex v0 = mesh.vertices[edge.P0];
Vertex v1 = mesh.vertices[edge.P1];
DrawLine(new Vector2((float)v0.x, (float)v0.y), new Vector2((float)v1.x, (float)v1.y), tx, Color.black);
}
}
tx.Apply();
this.GetComponent<Renderer>().sharedMaterial.mainTexture = tx;
}
// Bresenham line algorithm
private void DrawLine(Vector2 p0, Vector2 p1, Texture2D tx, Color c, int offset = 0)
{
int x0 = (int)p0.x;
int y0 = (int)p0.y;
int x1 = (int)p1.x;
int y1 = (int)p1.y;
int dx = Mathf.Abs(x1 - x0);
int dy = Mathf.Abs(y1 - y0);
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
int err = dx - dy;
while (true)
{
tx.SetPixel(x0 + offset, y0 + offset, c);
if (x0 == x1 && y0 == y1) break;
int e2 = 2 * err;
if (e2 > -dy)
{
err -= dy;
x0 += sx;
}
if (e2 < dx)
{
err += dx;
y0 += sy;
}
}
}
private float map(float from, float fromMin, float fromMax, float toMin, float toMax)
{
float fromAbs = from - fromMin;
float fromMaxAbs = fromMax - fromMin;
float normal = fromAbs / fromMaxAbs;
float toMaxAbs = toMax - toMin;
float toAbs = toMaxAbs * normal;
float to = toAbs + toMin;
return to;
}
}
Great news! I've managed to fix the issue. InsertVertex() doesn't actually add the new vertex to the list of vertices! So this means that when it tried to triangulate, it was trying to point to the new vertex but it couldn't (because that vertex wasn't in the list). So, to solve this, I just manually add my new vertex to the list of vertices in the mesh, before calling InsertVertex(). Note: When you do this, you also need to manually set the vertex's id. I set the id to the size of the list of vertices because I was adding all new vertices to the end of the list.
// When left click is pressed, a raycast is sent out. If that raycast hits the wall, updatePoints() is called and is passed in the location of the hit (hit.point).
public void updatePoints(Vector3 pos)
{
// Convert pos to local coords of wall. You don't need to do this, i do it because of my draw() method where i map everything out onto a texture and display it.
pos = transform.InverseTransformPoint(pos);
pos.x = map(pos.x, -0.5f, 0.5f, 0, 512);
pos.y = map(pos.y, -0.5f, 0.5f, 0, 512);
Vertex newVert = new Vertex(pos.x, pos.y);
// Manually add new vertex to list of vertices.
newVert.id = mesh.vertices.Count;
mesh.vertices.Add(newVert.id, newVert);
//Doing just the first line gave me a null pointer exception. Adding the two extra lines below it fixed it for me.
otri.tri = mesh.dummytri;
otri.orient = 0;
otri.Sym();
// Insert new vertex into existing triangulation.
mesh.InsertVertex(newVert, ref otri, ref osub, false, false);
// Draw result as a texture onto the wall so to visualise what is happening.
draw();
}
Hope this will help someone done the road!

Algorithm for generating a "ramp" object in Unity

I'm creating a basic simulator in Unity for my A-level Computer Science project. At the moment the user is able to draw a box (crate) object by selecting the associated tool and clicking and dragging to determine two opposite corners of the box, thus determining its dimensions.
The box consists of a single prefab which is instantiated and has its size changed accordingly. The code for it is as follows:
void Start () {
boxAnim = boxButton.GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
//sets the mouseDown and mouseHeld bools and the mouse position Vector3
mouseDown = Input.GetMouseButtonDown(0);
mouseHeld = Input.GetMouseButton(0);
mousePosition = Input.mousePosition;
//checks if the user has started to draw
if (mouseDown && !draw)
{
draw = true;
originalMousePosition = mousePosition;
}
//checking if the user has released the mouse
if (draw && !mouseHeld)
{
finalMousePosition = mousePosition;
draw = false;
if (boxAnim.GetBool("Pressed") == true) //if the box draw button is pressed
{
boxDraw(originalMousePosition, finalMousePosition); //draws crate
}
}
}
void boxDraw(Vector3 start, Vector3 end)
{
//asigns world coordinates for the start and end mouse positions
worldStart = Camera.main.ScreenToWorldPoint(start);
worldEnd = Camera.main.ScreenToWorldPoint(end);
if (worldStart.y >= -3.2f && worldEnd.y >= -3.2f)
{
//determines the size of box to be drawn
boxSize.x = Mathf.Abs(worldStart.x - worldEnd.x);
boxSize.y = Mathf.Abs(worldStart.y - worldEnd.y);
//crate sprite is 175px wide, 175/50 = 3.5 (50px per unit) so the scale factor must be the size, divided by 3.5
boxScaleFactor.x = boxSize.x / 3.5f;
boxScaleFactor.y = boxSize.y / 3.5f;
//initial scale of the box is 1 (this isn't necessary but makes reading program easier)
boxScale.x = 1 * boxScaleFactor.x;
boxScale.y = 1 * boxScaleFactor.y;
//creates a new crate under the name newBox and alters its size
GameObject newBox = Instantiate(box, normalCoords(start, end), box.transform.rotation) as GameObject;
newBox.transform.localScale = boxScale;
}
}
Vector3 normalCoords(Vector3 start, Vector3 end)
{
//takes start and end coordinates as position coordinates and returns a world coordinate coordinate for the box
if(end.x > start.x)
{
start.x = start.x + (Mathf.Abs(start.x - end.x) / 2f);
}
else
{
start.x = start.x - (Mathf.Abs(start.x - end.x) / 2f);
}
if(end.y > start.y)
{
start.y = start.y + (Mathf.Abs(start.y - end.y) / 2f);
}
else
{
start.y = start.y - (Mathf.Abs(start.y - end.y) / 2f);
}
start = Camera.main.ScreenToWorldPoint(new Vector3(start.x, start.y, 0f));
return start;
}
In a similar manner, I want to be able to have a 'ramp' object be able to be created, so that the user can click and drag to determine the base width, then click again to determine the angle of elevation/ height, (the ramp will always be a right angled triangle.) The problem lies in that I want to have the ramp as a sprite I have created, rather than just a basic block colour. A single sprite however would only have a single angle of elevation, and no transform would be able to alter this (as far as I'm aware.) Obviously I don't want to have to create a different sprite for each angle, so is there anything I can do?
The solution I was thinking was if there was some sort of feature whereby I could alter the nodes of a vector image in the code, but I'm pretty sure this doesn't exist.
EDIT: Just to clarify this is a 2D environment, the code includes Vector3s just because that’s what I’m used to
You mention Sprite which is a 2D object (well, its actually very much alike a Quad which counts as 3D) but you reference full 3D in other parts of your question and in your code, which I think was confusing people, because creating a texture for a sprite is a very different problem. I am assuming you mentioned Sprite by mistake and you actually want a 3D object (Unity is 3D internally most of the time anyways), it can only have one side if you want
You can create 3D shapes from code no problems, although you do need to get familiar with the Mesh class, and mastering creating triangles on the fly takes some practice
Here's a couple of good starting points
https://docs.unity3d.com/Manual/Example-CreatingaBillboardPlane.html
https://docs.unity3d.com/ScriptReference/Mesh.html
I have a solution to part of the problem using meshes and a polygon collider. I now have a function that will create a right angled triangle with a given width and height and a collider in the shape of that triangle:
using UnityEngine;
using System.Collections;
public class createMesh : MonoBehaviour {
public float width = 5f;
public float height = 5f;
public PolygonCollider2D polyCollider;
void Start()
{
polyCollider = GetComponent<PolygonCollider2D>();
}
// Update is called once per frame
void Update () {
TriangleMesh(width, height);
}
void TriangleMesh(float width, float height)
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//Verticies
Vector3[] verticies = new Vector3[3]
{
new Vector3(0,0,0), new Vector3(width, 0, 0), new Vector3(0,
height, 0)
};
//Triangles
int[] tri = new int[3];
tri[0] = 0;
tri[1] = 2;
tri[2] = 1;
//normals
Vector3[] normals = new Vector3[3];
normals[0] = -Vector3.forward;
normals[1] = -Vector3.forward;
normals[2] = -Vector3.forward;
//UVs
Vector2[] uv = new Vector2[3];
uv[0] = new Vector2(0, 0);
uv[0] = new Vector2(1, 0);
uv[0] = new Vector2(0, 1);
//initialise
mesh.vertices = verticies;
mesh.triangles = tri;
mesh.normals = normals;
mesh.uv = uv;
//setting up collider
polyCollider.pathCount = 1;
Vector2[] path = new Vector2[3]
{
new Vector2(0,0), new Vector2(0, height), new Vector2(width, 0)
};
polyCollider.SetPath(0, path);
}
}
I just need to put this function into code very similar to my code for drawing a box so that the user can specify width and height.

Map the custom triangle to Camera’s View Angle

I have make a triangle through my code. see below:
void TriangleMaker()
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//vertices
const int verticeCount = 3;//how many vertices for triangle its 3
Vector3[] vertices = new Vector3[verticeCount]{
//bottom left.orgin of the gameobject,
new Vector3(0,0,0),
new Vector3(width,0,0),
new Vector3(width, height,0)
};
//triangles
int[] tri = new int[6];
tri[0] = 0; //go clock wise always to make triangle from your vertices
tri[1] = 1;
tri[2] = 2;
//normals
//show the direction of objects
Vector3[] normals = new Vector3[3];
normals[0] = -Vector3.up;
normals[1] = -Vector3.up;
normals[2] = -Vector3.up;
mesh.vertices = vertices;
mesh.triangles = tri;
mesh.normals = normals;
}
The script is attached to child game-object of my camera and its showing the triangle but i want to map exactly my triangle to Camera’s view angle.
Also tried GeometryUtility.CalculateFrustumPlanes as Niki answer suggested and the result is not according to expectation:
2: https://docs.unity3d.com/ScriptReference/GeometryUtility.CalculateFrustumPlanes.html
Well I just found it in Unity docs:
https://docs.unity3d.com/ScriptReference/GeometryUtility.CalculateFrustumPlanes.html
P.S: I was so surprised to find this in docs. I think you are the only person in the world who needs this, finally. :D
EDIT:
Ok I edited your script. This visualizes camera's upper frustum plane. Note that this works in global space so move the camera at (0 0 0) position and (0 0 0) rotation to see the result. But I guess you can add the rest of the planes and turn this into local space yourself.
using UnityEngine;
using System.Collections;
public class FrustumScript : MonoBehaviour {
private void Start()
{
TriangleMaker();
}
void TriangleMaker()
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
Camera cam = Camera.main;
//vertices
Vector3[] vertices = new Vector3[3]
{
new Vector3(0, 0, 0),
cam.ViewportToWorldPoint(new Vector3(0, 1, cam.farClipPlane)),
cam.ViewportToWorldPoint(new Vector3(1, 1, cam.farClipPlane))
};
//triangles
int[] tri = new int[6];
tri[0] = 0; //go clock wise always to make triangle from your vertices
tri[1] = 1;
tri[2] = 2;
//normals
//show the direction of objects
Vector3[] normals = new Vector3[3];
normals[0] = -Vector3.up;
normals[1] = -Vector3.up;
normals[2] = -Vector3.up;
mesh.vertices = vertices;
mesh.triangles = tri;
mesh.normals = normals;
}
}

Unity C# generated mesh is offset why is it not in the correct location?

I am attempting to generate a mesh in unity from script. The mesh is generated by raycasting out in a particular direction. Then getting the vertices from the hit point or from where the ray terminates. The mesh generates fine and is working well, however the mesh generates approximately 5 to 10 unity units above the location of the object with the attached script. I will attach me script below.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Torch : MonoBehaviour {
public GameObject lightmeshholder;
private int RaysToShoot = 128;
private float distance = 50;
private Vector3[] vertices;
private Vector2[] vertices2d;
private int[] triangles;
private Mesh mesh;
private Texture2D texture;
private int screenwidth;
private int screenheight;
private int grab = 0;
private RaycastHit hit;
// Use this for initialization
void Start () {
screenwidth = Screen.width;
screenheight = Screen.height;
texture = new Texture2D (screenwidth, screenheight, TextureFormat.RGB24, false);
vertices = new Vector3[RaysToShoot];
vertices2d = new Vector2[RaysToShoot];
triangles = new int[(RaysToShoot) +1 ];
mesh= lightmeshholder.GetComponent<MeshFilter>().mesh;
}
// Update is called once per frame
void Update () {
float angle =0;
for(int i=0;i<RaysToShoot;i++){
float x = Mathf.Sin(0);
x=-5;
if(Input.GetKey(KeyCode.P)){
x = 5;
}
float y = Mathf.Cos(angle);
if (angle <= 90){
angle += 2*Mathf.PI/RaysToShoot;
}
Vector3 dir = new Vector3(x,y,0);
if (Physics.Raycast (this.transform.position, dir,out hit, distance))
{
Debug.DrawLine (this.transform.position, hit.point,new Color(1,1,0,1));
Vector3 tmp = lightmeshholder.transform.InverseTransformPoint(hit.point);
vertices2d[i] = new Vector2(tmp.x,tmp.y);
}else{
Vector3 tmp = lightmeshholder.transform.InverseTransformPoint(this.transform.position + dir*distance);
vertices2d[i] = new Vector2(tmp.x,tmp.y);
Debug.DrawLine(this.transform.position,dir * distance,Color.red,0);
}
}
// build mesh
Vector2[] uvs = new Vector2[vertices2d.Length +1];
Vector3[] newvertices = new Vector3[vertices2d.Length+1];
for (int n = 0; n<newvertices.Length-1 ;n++)
{
if(n==0){
newvertices[0]=this.transform.position;
newvertices[1] = vertices2d[0];
uvs[0] = new Vector2(this.transform.position.x,this.transform.position.y);
uvs[1] = vertices2d[0];
}else{
newvertices[n+1] = vertices2d[n];
uvs[n+1] = vertices2d[n];
}
if(n==0){
triangles[0] = 0;
triangles[1] = 1;
triangles[2] = 2;
}else if(n<newvertices.Length/3){
triangles[n*3] = 0;
triangles[1+n*3] = n+1;
triangles[2+n*3] = n+2;
}
}
Mesh mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
mesh.Clear();
mesh.vertices = newvertices;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
}
The hitpoints you're getting from the raycasts are global. You can either:
1) Make sure the mesh is in a gameobject that is positioned at 0,0,0. Whenever I create a dynamic mesh based on ray hitpoints I usually create a new parent-less gameobject at 0,0,0 and add the mesh to it. (instead of getting a container GameObject externally)
2) Call lightmeshholder.transform.InverseTransformPoint on the hitpoints from the raycast before building the mesh
When you are assigning vertices back to mesh they are calculated in local coordinates with respect to object root. So if you want to bring it back "where it should be" process them with .TransformPoint() call to get proper offsets. This should fix your problem with them being in different location than you expect. I had same problem.

Categories