I've been trying to create a plane mesh in Unity using code, and I've come across a very interesting problem. I created an int[], filled it up with some values, and it's length is somehow zero. I've never encountered anything this quirky, so I'd enjoy a bit of help.
mesh.triangles = new int[]
{
4, 6, 5, 5, 6, 7
};
... // Not important stuff
Debug.Log(mesh.triangles.Length);
I don't know what is happening, so I really haven't tried anything. But in the console, there is an error message stating Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 6, VertexCount: 4.This is probably really important, but I don't understand some parts of the message(especially the last part). And if it makes a difference, I have an array concatenation method being called to add the first triangles to these ones. I initially identified this problem when the half of my mesh still wasn't appearing. I would really appreciate help; thanks.
Edit:
To cut confusion, I'm just going to paste my whole entire method.
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
mesh.triangles = new int[]
{
4, 6, 5, 5, 6, 7
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
Debug.Log(mesh.triangles.Length);
}
}
You only have FOUR vertices!
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
So the indices 4, 6, 5, 5, 6, 7 are all invalid! If you have only four vertices you can maximum have the indices 0, 1, 2, 3
=> Unity simply rejects them all. You should have already taken that hint from the error you get
Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 6, VertexCount: 4
Now it is a bit unclear what exactly you are trying to achieve here but
either you want to REPLACE the vertices: In this case there is no reason to set new triangle instances etc at all! It is enough to connect them only once:
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero + offset,
Vector3.right + offset,
Vector3.forward + offset,
new Vector3(1, 0, 1) + offset
};
}
}
the other properties can simply be left untouched since you only want to update the vertex positions.
Or you actually wanted to ADD more faces. In that case you rather want to append to the existing arrays:
private void CreateQuad(ref Mesh mesh, Vector3 offset, bool first)
{
if (first)
{
mesh.vertices = new Vector3[]
{
Vector3.zero, Vector3.right, Vector3.forward, new Vector3(1, 0, 1)
};
mesh.triangles = new int[]
{
0, 2, 1, 1, 2, 3
};
mesh.normals = new Vector3[]
{
Vector3.back, Vector3.back, Vector3.back, Vector3.back
};
mesh.tangents = new Vector4[]
{
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1),
new Vector4(1, 0, 0, -1)
};
mesh.uv = new Vector2[]
{
Vector2.zero, Vector2.right, Vector2.up, Vector2.one
};
}
else if (!first)
{
// fist get already existing verts etc
var oldVerts = mesh.vertices;
var oldTris = mesh.triangles;
// create new vertices and triangles arrays with additional space for the new quad
var newVerts = new Vector3[oldVerts.Length + 4];
var newTris = new int[oldTris.Length + 6];
// copy over the existing vertices and triangles
Array.Copy(oldVerts, newVerts, olVerts.Length);
Array.Copy(oldTris, newtris, oldtris.Length);
// then append the new vertices
newVerts[oldverts.Length + 0] = Vector3.zero + offset;
newVerts[oldverts.Length + 1] = Vector3.right + offset;
newVerts[oldverts.Length + 2] = Vector3.forward + offset;
newVerts[oldverts.Length + 3] = new Vector3(1, 0, 1) + offset;
// append the new triangles
newTris[oldTris.Length + 0] = oldverts.Length + 0;
newTris[oldTris.Length + 1] = oldverts.Length + 2;
newTris[oldTris.Length + 2] = oldverts.Length + 1;
newTris[oldTris.Length + 3] = oldverts.Length + 1;
newTris[oldTris.Length + 4] = oldverts.Length + 2;
newTris[oldTris.Length + 5] = oldverts.Length + 3;
// get the min and max points for filling the uvs (not the most efficient way probably but it is what it is ^^)
// we later want to spread out the UV values linear between 0 (min) and 1 (max) on the given vertices
var min = Vector3.zero;
var max = Vector3.zero;
foreach(var vertex in newVerts)
{
min = Vector3.Min(min, vertex);
max = Vector3.Max(max, vertex);
}
// also fill new tangents and normals and uvs (if really necessary)
var newNormals = new Vector3[newVerts.Length];
var newTangents = new Vector4[newVerts.Length];
var newUVs = new Vector2[newVerts.Length];
for(var i = 0; i < newVerts.Length; i++)
{
var vertex = newVerts[i];
newUVs[i] = new Vector2((vertex.x - min.x) / (max.x - min.x), (vertex.z - min.z) / (max.z - min.z));
newNormals[i] = Vector3.back;
newTangents[i] = new Vector4(1, 0, 0, -1);
};
// finally set them all back
mesh.vertices = newVerts;
mesh.triangles = newTris;
mesh.normals = newNormals;
mesh.tangents = newTangents;
mesh.uv = newUs;
}
}
You first need to set the vertex array before altering the triangles. As Unity writes "It is recommended to assign a triangle array after assigning the vertex array, in order to avoid out of bounds errors."
mesh.vertices = new Vector3[] { new Vector3(-1,0,1), new Vector3(-1,0,-1),
new Vector3(1,0,-1), new Vector3(1,0,1) };
mesh.triangles = new int[] {0,1,2,0,2,3};
I've created a square using linerenderer but my problem now is that the line is not smooth. see below picture. I've tried enabling anti-aliasing and setting lr.numCapVertices to no avail.
private void DrawLine()
{
GameObject myLine = new GameObject();
myLine.transform.position = start;
myLine.AddComponent<LineRenderer>();
lr = myLine.GetComponent<LineRenderer>();
lr.material = new Material(Shader.Find("Sprites/Default"));
lr.positionCount = 4;
lr.startWidth = .1f;
lr.endWidth = .1f;
lr.SetPosition(0, new Vector3(1, 0, 0));
lr.SetPosition(1, new Vector3(2, 0, 0));
lr.SetPosition(2, new Vector3(2, -1, 0));
lr.SetPosition(3, new Vector3(1, -1, 0));
lr.loop = true;
Gradient gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[]
{
new GradientColorKey(Color.red, 0.25f),
new GradientColorKey(Color.green, 0.50f),
new GradientColorKey(Color.blue, 0.75f),
new GradientColorKey(Color.yellow, 1f),
},
new GradientAlphaKey[] { new GradientAlphaKey(1f, 0.0f)
}
);
lr.colorGradient = gradient;
}
Have a look at line renderer documentation. Specifically the following properties:
Corner vertices
End cap vertices
Alignment
The corner and cap vertices will determine the 'roundness' of corners and line endings.
Different alignment settings may give you a more orthogonal view of the line which should prevent the illusion of varying thickness as can be seen at the green corner in your image.
I initially had a function that created a rectangular mesh given a specific height/width:
void BoxMesh(float width, float height)
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//Verticies
Vector3[] verticies = new Vector3[4]
{
new Vector3(0,0,0), new Vector3(0, height, 0), new Vector3(width, height, 0), new Vector3(width, 0, 0)
};
//Triangles
int[] tri = new int[6];
tri[0] = 0;
tri[1] = 1;
tri[2] = 3;
tri[3] = 1;
tri[4] = 2;
tri[5] = 3;
//normals
Vector3[] normals = new Vector3[4];
normals[0] = -Vector3.forward;
normals[1] = -Vector3.forward;
normals[2] = -Vector3.forward;
normals[3] = -Vector3.forward;
//UVs
Vector2[] uv = new Vector2[4];
uv[0] = new Vector2(0, 0);
uv[1] = new Vector2(0, 1);
uv[2] = new Vector2(1, 1);
uv[3] = new Vector2(1, 0);
//initialise
mesh.vertices = verticies;
mesh.triangles = tri;
mesh.normals = normals;
mesh.uv = uv;
//setting up collider
polyCollider.pathCount = 1;
Vector2[] path = new Vector2[4]
{
new Vector2(0,0), new Vector2(0, height), new Vector2(width, height), new Vector2(width, 0)
};
polyCollider.SetPath(0, path);
}
I then decided I wanted this mesh to have an outline so I edited the function so it is now:
void BoxMesh(float width, float height, float OLwidth)
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
MeshFilter mfOL = GetComponent<MeshFilter>();
Mesh meshOL = new Mesh();
mfOL.mesh = meshOL;
//Verticies
Vector3[] verticies = new Vector3[4]
{
new Vector3(0,0,0), new Vector3(0, height, 0), new Vector3(width, height, 0), new Vector3(width, 0, 0)
};
//Verticies Outline
Vector3[] verticiesOL = new Vector3[4]
{
new Vector3(-OLwidth,-OLwidth,0), new Vector3(-OLwidth, height + OLwidth, 0), new Vector3(width + OLwidth, height + OLwidth, 0), new Vector3(width + OLwidth, -OLwidth, 0)
};
//Triangles
int[] tri = new int[6];
tri[0] = 0;
tri[1] = 1;
tri[2] = 3;
tri[3] = 1;
tri[4] = 2;
tri[5] = 3;
//normals
Vector3[] normals = new Vector3[4];
normals[0] = -Vector3.forward;
normals[1] = -Vector3.forward;
normals[2] = -Vector3.forward;
normals[3] = -Vector3.forward;
//UVs
Vector2[] uv = new Vector2[4];
uv[0] = new Vector2(0, 0);
uv[1] = new Vector2(0, 1);
uv[2] = new Vector2(1, 1);
uv[3] = new Vector2(1, 0);
//initialise
mesh.vertices = verticies;
mesh.triangles = tri;
mesh.normals = normals;
mesh.uv = uv;
meshOL.vertices = verticiesOL;
meshOL.triangles = tri;
meshOL.normals = normals;
meshOL.uv = uv;
//setting up collider
polyCollider.pathCount = 1;
Vector2[] path = new Vector2[4]
{
new Vector3(-OLwidth,-OLwidth,0), new Vector3(-OLwidth, height + OLwidth, 0), new Vector3(width + OLwidth, height + OLwidth, 0), new Vector3(width + OLwidth, -OLwidth, 0)
};
polyCollider.SetPath(0, path);
}
So i now have two meshes one outside the other. However I want the one on the outside to have a different material: so it looks like an outline.
I have these two materials in my mesh renderer, so how would I go about assigning one material to the outer mesh, and another to the inner mesh.
The concept you might be missing here is "SubMesh", each submesh of a mesh will be rendered using a material from the list at a given index.
To create a submesh you can call
mesh.SetTriangles(int[] triangles, int submesh);
You can re-use your vertexes, by specifying different faces basing on the same list of points. If you specify more submeshes than materials, remaining submeshes will be rendered solid magenta.
I am working on a 3D application in WPF and having trouble with the camera. It should be possible to rotate the camera around its own axis (with other words, look around) using the mouse but I can not get it to work properly. I create the camera with the following code:
PerspectiveCamera perspectiveCamera = new PerspectiveCamera(new Point3D(0, 30, 0), new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), 90);
perspectiveCamera.NearPlaneDistance = 0.001;
perspectiveCamera.FarPlaneDistance = 1000;
center = new TranslateTransform3D(0, 30, 0);
rot_x = new AxisAngleRotation3D(new Vector3D(1, 0, 0), 0);
rot_y = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
rot_z = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0);
zoom = new ScaleTransform3D(1, 1, 1);
Transform3DGroup t = new Transform3DGroup();
t.Children.Add(zoom);
t.Children.Add(new RotateTransform3D(rot_x));
t.Children.Add(new RotateTransform3D(rot_y));
t.Children.Add(new RotateTransform3D(rot_z));
t.Children.Add(center);
perspectiveCamera.Transform = t;
myViewport3D.Camera = perspectiveCamera;
And then I try to rotate it using the following code:
private void OnViewportMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (mouseLeftIsDown)
{
Point position = e.GetPosition(this);
double rotZAngle = (rot_z.Angle % 360) + oldLeftPosition.X - position.X;
double rotXAngle = (rot_x.Angle % 360) + position.Y - oldLeftPosition.Y;
if (rotZAngle < 0) //rotZAngle is negative, make it positive
{
rotZAngle = 360 + rotZAngle;
}
if (rotXAngle < 0) //rotXAngle is negative, make it positive
{
rotXAngle = 360 + rotXAngle;
}
rot_z.Angle = rotZAngle;
rot_x.Angle = rotXAngle;
oldLeftPosition = position;
}
}
However, it seams that the rotation is not happening around the camera and instead somehere else. The model that I load at position (0,0,0) is becoming visible after I rotate 180 degrees around the z axis which should not be the case.
What am I missing?
I'm creating a bounding box and for debugging would like to see where it is. It is define as four corners and I simply want to draw line in between these 4 corners.
I managed to Google how to do this:
public void Draw(GraphicsDevice graphicsDevice)
{
int num = mCorners.Length;
VertexPositionColor[] primitiveList = new VertexPositionColor[num];
for (int i = 0; i < num; ++i)
{
primitiveList[i] = new VertexPositionColor(new Vector3(mCorners[i], 0), Color.White);
}
short[] triangleStripIndices = new short[] { 0, 1, 2, 3, };
graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, primitiveList, 0, 4, triangleStripIndices, 0, 3);
}
However, when this code is ran the app just closes. It is in debug mode but there is no stack trace, crash message, error log etc. It just closes, making it very hard to debug.
I've found a similar question that doesn't have an answer:
XNA 4.0 app closes suddenly when it hits a method without throwing any exceptions. The suggestion was is it being initialised properly, and yes mine is. The GraphicsDevice is passed around as a parameter and not obtained statically.
Does anybody know what may be causing this?
Thanks,
Hi Luke Try TO Put You Code In Dispatcher Like This
this.Dispatcher.BeginInvoke(new System.Action(delegate()
{
int num = mCorners.Length;
VertexPositionColor[] primitiveList = new VertexPositionColor[num];
for (int i = 0; i < num; ++i)
{
primitiveList[i] = new VertexPositionColor(new Vector3(mCorners[i], 0), Color.White);
}
short[] triangleStripIndices = new short[] { 0, 1, 2, 3, };
graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, primitiveList, 0, 4, triangleStripIndices, 0, 3);
}));
It Seems System Got Crashes When UI Need Changes And Prcoess is Still Working.. And It Throws Unhandled Exception.
Ok, figured it out. Code is as follows:
private BasicEffect mBasicEffect;
public void Draw(GraphicsDevice graphicsDevice)
{
// If we haven't set this up yet then do so now
if (mBasicEffect == null)
{
CreateBasicEffect(graphicsDevice);
}
foreach (EffectPass pass in mBasicEffect.CurrentTechnique.Passes)
{
pass.Apply();
int num = mCorners.Length + 1;
short[] triangleStripIndices = new short[num];
VertexPositionColor[] primitiveList = new VertexPositionColor[num];
for (int i = 0; i < num; ++i)
{
int index = i % mCorners.Length;
Vector2 vec = mCorners[index];
primitiveList[index] = new VertexPositionColor(new Vector3(vec, 0), Color.White);
triangleStripIndices[i] = (short)i;
}
graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, primitiveList, 0, 5, triangleStripIndices, 0, 4);
}
}
private void CreateBasicEffect(GraphicsDevice device)
{
mBasicEffect = new BasicEffect(device);
mBasicEffect.VertexColorEnabled = true;
Matrix viewMatrix = Matrix.CreateLookAt(new Vector3(0, 0, 1), Vector3.Zero, Vector3.Up);
Matrix worldMatrix = Matrix.CreateTranslation(0, 0, 0);
Matrix projectionMatrix = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 1000);
mBasicEffect.World = worldMatrix;
mBasicEffect.View = viewMatrix;
mBasicEffect.Projection = projectionMatrix;
}