- Home /
Procedurally created mesh appearing offset from it's vert locations.
Hey all.
I'm building a mesh procedurally at runtime and it works fine. but the visible mesh and the mesh collider are located offset from the location of the vertices.
To check this, I ran a for loop to grab all the vertices in the mesh and instantiated a small cube at each location.
Here's a picture showing an example:
In this, the cube at the bottom right is being used as an indicator of where world origin is.
As you can see, the mesh is in one location, while the verts (represented by the smaller cubes) is in another. The gameObject the mesh is a part of is created at the mouse position in world space using a ray (which works fine for placing primitives, so it doesn't appear to be the culprit).
The further away the mesh is created from world origin, the larger the offset. As you may have deduced from the image, the direction of the offset is also related to the world origin, always being in the opposite direction.
This here is the relevant bit of code for creating the mesh. New sets of points are added and calculated as you move the mouse around (with the button held down), creating a wall as you go.
IEnumerator StartWall()
{
makeWall = true;
oldMousePosition = Input.mousePosition;
wallNumber += 1;
bool startedMoving = false;
List<Vector3> currentPoints = new List<Vector3>();
List<Vector3> allPoints = new List<Vector3>();
List<int> currentTris = new List<int>();
List<int> allTris = new List<int>();
int segments = 0;
////INITILIZATION////
#region Initializing The Wall Object
mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(mouseRay, out mouseHit, 500f, floorMask))
{
startPoint = mouseHit.point;
}
Vector3 previousPoint = startPoint;
Vector3 nextPoint = startPoint;
Transform newWall = new GameObject("Wall # " + wallNumber).transform;
newWall.position = startPoint;
newWall.parent = wallContainer;
walls.Add(newWall);
MeshFilter newWallMeshFilter = newWall.gameObject.AddComponent<MeshFilter>();
MeshRenderer newWallRenderer = newWall.gameObject.AddComponent<MeshRenderer>();
MeshCollider newWallCollider = newWall.gameObject.AddComponent<MeshCollider>();
newWallRenderer.material = shinyWhite;
Mesh newWallMesh = new Mesh();
newWallMesh.name = "wall_mesh " + wallNumber;
newWallMeshFilter.mesh = newWallMesh;
newWallCollider.sharedMesh = newWallMesh;
#endregion
while (makeWall)
{
if (mousePosition != oldMousePosition)
{
startedMoving = true;
//Mesh Creation
mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(mouseRay, out mouseHit, 500f, floorMask))
{
if(Vector3.Distance(mouseHit.point, previousPoint) > smoothness)
{
nextPoint = mouseHit.point;
Instantiate(cubePrimitive, nextPoint, Quaternion.identity);
Vector3 direction = (nextPoint - previousPoint).normalized;
Vector3 up = new Vector3(0, 1, 0).normalized;
Vector3 right = Vector3.Cross(direction, up);
//Front
Vector3 point0 = previousPoint;
Vector3 point1 = nextPoint;
Vector3 point2 = new Vector3(nextPoint.x, nextPoint.y + wallHeight, nextPoint.z);
Vector3 point3 = new Vector3(previousPoint.x, previousPoint.y + wallHeight, previousPoint.z);
//Top
Vector3 point4 = new Vector3(previousPoint.x, previousPoint.y + wallHeight, previousPoint.z);
Vector3 point5 = new Vector3(nextPoint.x, nextPoint.y + wallHeight, nextPoint.z);
Vector3 point6 = new Vector3(nextPoint.x, nextPoint.y + wallHeight, nextPoint.z) + (right * wallWidth);
Vector3 point7;
if (allPoints.Count < 1)
{
point7 = new Vector3(previousPoint.x, previousPoint.y + wallHeight, previousPoint.z) + (right * wallWidth);
}
else
{
point7 = allPoints[allPoints.Count - 6];
}
//Back
Vector3 point8 = new Vector3(point6.x, point6.y - wallHeight, point6.z);
Vector3 point9 = new Vector3(point7.x, point7.y - wallHeight, point7.z);
Vector3 point10 = point7;
Vector3 point11 = point6;
currentPoints = new List<Vector3>()
{
//Front
point0, point1, point2, point3,
//Top
point4, point5, point6, point7,
//Back
point8, point9, point10, point11,
};
allPoints.AddRange(currentPoints);
allPointsTest = allPoints;
newWallMesh.vertices = allPoints.ToArray();
segments++;
currentTris = new List<int>()
{
0 + ((segments - 1) * 12), 2 + ((segments - 1) * 12), 1 + ((segments - 1) * 12),
0 + ((segments - 1) * 12), 3 + ((segments - 1) * 12), 2 + ((segments - 1) * 12),
4 + ((segments - 1) * 12), 6 + ((segments - 1) * 12), 5 + ((segments - 1) * 12),
4 + ((segments - 1) * 12), 7 + ((segments - 1) * 12), 6 + ((segments - 1) * 12),
8 + ((segments - 1) * 12), 10 + ((segments - 1) * 12), 9 + ((segments - 1) * 12),
8 + ((segments - 1) * 12), 11 + ((segments - 1) * 12), 10 + ((segments - 1) * 12),
};
allTris.AddRange(currentTris);
newWallMesh.triangles = allTris.ToArray();
newWallMesh.RecalculateNormals();
newWallMesh.RecalculateBounds();
newWallMesh.Optimize();
newWallCollider.sharedMesh = null;
newWallCollider.sharedMesh = newWallMesh;
previousPoint = nextPoint; //Assigns the current point(nextPoint) to the previousPoint variable for use in the next iteration.
}
}
oldMousePosition = mousePosition;
}
Answer by maccabbe · Oct 03, 2015 at 05:44 PM
Are you taking the transform into account?
The mesh stores the position of each vertex in local space while the displayed position adds the transform information on top of it. So a simple case would be a vertex with coordinates (0, 0, 0) in a transform where the position is (1, 0, 0) would display the vertex at (1, 0, 0).
To make cubes where each vertex is displayed you're going to have to line up their world coordinates. One way to do so would be to replace
Instantiate(cubePrimitive, nextPoint, Quaternion.identity);
with
Instantiate(cubePrimitive, transform.TransformPoint(nextPoint), Quaternion.identity)
Thanks for your reply!
The cubes were just for debugging purposes, to check where the verts were.
Since the mesh is created starting at the mouse point in world space, and then following it, it means that the visible object isn't anywhere near the mouse position, so I need to line up what is visible with the actual location of the verts.
Following your advice about the transform position being added on to it, however, I subtracted the objects position from the vert locations as I added them into the array. This worked really well and achieved exactly what I was hoping for, except for three verts where doing this threw them off. Not adjusting them caused them to behave, though I don't know why.
Regardless, this solved my issue. Thanks!
"subtracted"? That only works if the gameobject that has the $$anonymous$$eshRenderer for your mesh is not rotated or scaled. In general you should use transform.InverseTransformPoint
to transform a worldspace position into localspace.
Hi there, thanks for the response. The mesh in question is being created from scratch and isn't something that needs to move, so it's fine always having the same rotation and scale. To future proof myself though, I'll mess around with that and see what I can do. Thanks!