Physics.Raycast not working with a Marching Cubes -generated mesh - c#

What it spits out
My raycast spits out a position way off from what it's supposed to be. I'm trying to place objects procedurally on a procedural mesh. I've been scratching my head at this for a while. Please help.
Sorry for the long script.
The start of the code is just some declares and stuff. GenObjects is run once in FixedUpdate after Start has finished. I'm using a marching cubes library by Scrawk and a noise library by Auburn
void GenMesh()
{
Marching marching = new MarchingCubes();
marching.Surface = 0.0f;
voxels = new float[width * height * length];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < length; z++)
{
float fx = x / (width - 1.0f);
float fy = y / (height - 1.0f);
float fz = z / (length - 1.0f);
int idx = x + y * width + z * width * height;
float surfaceHeight = noise2.GetNoise(x,z) * amplitude + offset;
float currentHeight = Mathf.Clamp(y, surfaceHeight - threshold, surfaceHeight + threshold);
float t = Mathf.Abs(currentHeight - surfaceHeight) / threshold;
voxels[idx] = Mathf.Lerp(Mathf.Clamp(noise.GetNoise(x,y,z), 0.65f, 1), -1f, t);
}
}
}
List<Vector3> verts = new List<Vector3>();
List<int> indices = new List<int>();
marching.Generate(voxels, width, height, length, verts, indices);
int maxVertsPerMesh = 30000;
int numMeshes = verts.Count / maxVertsPerMesh + 1;
for (int i = 0; i < numMeshes; i++)
{
List<Vector3> splitVerts = new List<Vector3>();
List<int> splitIndices = new List<int>();
for (int j = 0; j < maxVertsPerMesh; j++)
{
int idx = i * maxVertsPerMesh + j;
if (idx < verts.Count)
{
splitVerts.Add(verts[idx]);
splitIndices.Add(j);
}
}
if (splitVerts.Count == 0) continue;
Mesh mesh = new Mesh();
mesh.SetVertices(splitVerts);
mesh.SetTriangles(splitIndices, 0);
mesh.RecalculateBounds();
mesh.RecalculateNormals();
MeshWelder meshWelder = new MeshWelder(mesh);
meshWelder.Weld();
GameObject go = new GameObject("Mesh");
go.layer = LayerMask.NameToLayer("Ground");
go.transform.parent = transform;
go.transform.localScale = new Vector3(100f, 100f, 100f);
go.AddComponent<MeshFilter>();
go.AddComponent<MeshRenderer>();
go.AddComponent<MeshCollider>();
go.GetComponent<Renderer>().material = m_material;
go.GetComponent<MeshFilter>().mesh = mesh;
go.GetComponent<MeshCollider>().sharedMesh = mesh;
go.GetComponent<MeshCollider>().contactOffset = 0f;
go.transform.localPosition = new Vector3(-width * 100 / 2, -height * 100 / 4, -length * 100 / 2);
meshes.Add(go);
}
}
void GenObjects(GameObject prefab, float radius, Vector2 sampleRegionSize, Vector2 origin, int seed)
{
List<Vector2> points = PoissonDiscSampling.GeneratePoints(radius, sampleRegionSize, seed);
Physics.queriesHitBackfaces = true;
foreach (Vector2 point in points)
{
RaycastHit hit;
Vector3 objPos = new Vector3(0,0,0);
bool validPosFound = false;
if (Physics.Raycast(new Vector3(point.x + origin.x, 0, point.y + origin.y), Vector3.down, out hit, height * 100, layerMask))
{
objPos = hit.point;
validPosFound = true;
} else if (Physics.Raycast(new Vector3(point.x + origin.x, 0, point.y + origin.y), Vector3.up, out hit, height * 100, layerMask))
{
objPos = hit.point;
validPosFound = true;
}
if (validPosFound)
{
GameObject newObject = Instantiate(prefab, objPos, Quaternion.Euler(0, 0, 0));
}
}
Physics.queriesHitBackfaces = false;
}
}

Fixed! My mistake was really stupid. I wasn't assigning the welded mesh, leaving a filthy mesh with lots of empty verts floating about. The raycast was hitting them.
The fixed lines for anyone who cares:
Mesh mesh = new Mesh();
Mesh mesh_temp = new Mesh();
mesh_temp.SetVertices(splitVerts);
mesh_temp.SetTriangles(splitIndices, 0);
mesh_temp.RecalculateBounds();
mesh_temp.RecalculateNormals();
MeshWelder meshWelder = new MeshWelder();
meshWelder.customMesh = new CustomMesh();
meshWelder.customMesh.vertices = splitVerts.ToArray();
meshWelder.customMesh.triangles = splitIndices.ToArray();
meshWelder.customMesh.normals = mesh_temp.normals;
meshWelder.Weld();
mesh.SetVertices(meshWelder.customMesh.vertices);
mesh.SetTriangles(meshWelder.customMesh.triangles, 0);
mesh.SetNormals(meshWelder.customMesh.normals);
mesh.RecalculateBounds();

Related

Mandelbrot Set function crashes

i made a sim for the Mandelbrot Set function, zn + 1 = power(zn) + c
and it work but when i get to the point were the function is unstable it crashes, now i have a boolen that when true makes a wire that connects all the circles, when its false its fine(dosent crash) but when its on it does, the code works like this:
start:
building a list of circles and making there pos by the equation, and then crating a wire between the circle and the last circle,
update:
then when you move the circle it uses the already made list of gameobj to update there pos.
you can try it here:
build
github:
git
but it crashes:(, heres the code:
private void updateCircles()
{
StartUpdateCircles();
}
private void StartCircles()
{
float x = BlackCircle.anchoredPosition.x;
float y = BlackCircle.anchoredPosition.y;
AllCircles.Add(BlackCircle.gameObject);
for (int i = 1; i < itarations; i++)
{
Vector2 RedCircleVec2 = RedCircle.anchoredPosition;
Vector2 LastCircleVec2 = AllCircles[i - 1].GetComponent<RectTransform>().anchoredPosition;
GameObject Circle = Instantiate(BlackCircle.gameObject, Vector3.zero, Quaternion.identity);
Circle.transform.SetParent(CanvasPerent);
AllCircles.Add(Circle);
x = Mathf.Pow(x, 2);
x -= Mathf.Pow(LastCircleVec2.y, 2);
x += RedCircleVec2.x;
y = (2 * LastCircleVec2.x
* LastCircleVec2.y) + RedCircleVec2.y;
Circle.GetComponent<RectTransform>().anchoredPosition = new Vector2(x, y);
if (HasWire)
{
GameObject wire = GenrateWireStart(LastCircleVec2
, Circle.GetComponent<RectTransform>().anchoredPosition);
AllWires.Add(wire);
}
}
}
private void StartUpdateCircles()
{
float x = BlackCircle.anchoredPosition.x;
float y = BlackCircle.anchoredPosition.y;
for (int i = 1; i < itarations; i++)
{
Vector2 RedCircleVec2 = RedCircle.anchoredPosition;
Vector2 LastCircleVec2 = AllCircles[i - 1].GetComponent<RectTransform>().anchoredPosition;
RectTransform ICircle = AllCircles[i].GetComponent<RectTransform>();
x = Mathf.Pow(x, 2);
x -= Mathf.Pow(LastCircleVec2.y, 2);
x += RedCircleVec2.x;
y = (2 * LastCircleVec2.x
* LastCircleVec2.y) + RedCircleVec2.y;
ICircle.anchoredPosition = new Vector2(x, y);
if (HasWire)
{
GenrateWireUpdate(LastCircleVec2
,ICircle.anchoredPosition, i);
}
}
}
public GameObject GenrateWireStart(Vector2 NodeA, Vector2 NodeB)
{
GameObject Connector = new GameObject("connector", typeof(Image));
Connector.transform.SetParent(CanvasPerent);
RectTransform ConnectorRT = Connector.GetComponent<RectTransform>();
ConnectorRT.anchorMin = new Vector2(0, 0);
ConnectorRT.anchorMax = new Vector2(0, 0);
Connector.GetComponent<Image>().color = new Color(0f, 0f, 0f, 0.25f);
Vector2 dir = (NodeB - NodeA).normalized;
float distance = Vector2.Distance(NodeA, NodeB);
ConnectorRT.sizeDelta = new Vector2(distance, 0.005f);
ConnectorRT.position = NodeA + dir * distance * .5f;
ConnectorRT.localEulerAngles = new Vector3(0, 0, UtilsClass.GetAngleFromVectorFloat(dir));
return Connector;
}
public void GenrateWireUpdate(Vector2 NodeA, Vector2 NodeB, int i)
{
RectTransform ConnectorRT = AllWires[i - 1].GetComponent<RectTransform>();
Vector2 dir = (NodeB - NodeA).normalized;
float distance = Vector2.Distance(NodeA, NodeB);
ConnectorRT.sizeDelta = new Vector2(distance, 0.005f);
ConnectorRT.position = NodeA + dir * distance * .5f;
ConnectorRT.localEulerAngles = new Vector3(0, 0, UtilsClass.GetAngleFromVectorFloat(dir));
}
pls help, thank you.
I looked briefly into your code and you seem to get some invalid positions like infinite / undefined from your calculations or just some positions too far away for Unity.
I could remove these by simply limiting positions to e.g.
x = Mathf.Clamp(Mathf.Pow(x, 2), -Screen.width, Screen.width);
x = Mathf.Clamp(x - Mathf.Pow(LastCircleVec2.y, 2), -Screen.width, Screen.width);
x = Mathf.Clamp(x + RedCircleVec2.x, -Screen.width, Screen.width);
y = Mathf.Clamp((2 * LastCircleVec2.x * LastCircleVec2.y) + RedCircleVec2.y, -Screen.width, Screen.width);
which simply limits all positions to some off-screen max positions

Unity Map generation and colors

I am currently following a Brackeys tutorial on procedural terrain generation colors. I got to a point where it gives me this error:
IndexOutOfRangeException: Index was outside the bounds of the array. mapgeneration.CreateShape () (at Assets/mapgeneration.cs:108
mapgeneration.Update () (at Assets/mapgeneration.cs:131)
I am using gradients to display color on line 108 in CreateShape. This is the line:
colors[iloopedforvertecy] = gradient.Evaluate(height);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class mapgeneration : MonoBehaviour
{
Mesh mesh;
Color[] colors;
Vector3[] vertices;
int[] triangles;
public int xsize = 20;
public int zsize = 20;
[Range(1, 100)]
public float smooth = 1.0f;
public MeshCollider _mesh;
public Transform water;
public float scale;
public float smoothfactor;
public float xoffset = 0.0f;
public float zoffset = 0.0f;
public float minwaterheight;
public float maxwaterheight;
public float minterainheight;
public float maxterainheight;
public Gradient gradient;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
_mesh = GetComponent<MeshCollider>();
water.transform.position = new Vector3(0, Random.Range(minwaterheight, maxwaterheight), 0);
}
void CreateShape()
{
vertices = new Vector3[(xsize + 1) * (zsize + 1)];
water.transform.localScale = new Vector3(xsize, 0, zsize);
int iloopedforvertecy = 0;
triangles = new int[xsize * zsize * 6];
int vert = 0;
int tris = 0;
for(int z = 0; z < zsize; z++)
{
for(int x = 0; x < xsize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xsize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xsize + 1;
triangles[tris + 5] = vert + xsize + 2;
vert++;
tris += 6;
}
vert++;
}
colors = new Color[vertices.Length];
for (int z = 0; z <= zsize; z++)
{
for(int x = 0; x <= xsize; x++)
{
float xCoord = (float)x / xsize * scale + xoffset;
float zCoord = (float)z / zsize * scale + zoffset;
float y = Mathf.PerlinNoise(xCoord * smooth, zCoord * smooth) * smoothfactor;
vertices[iloopedforvertecy] = new Vector3(x, y, z);
iloopedforvertecy += 1;
if(y > maxterainheight)
{
maxterainheight = y;
}
if(y < minterainheight)
{
minterainheight = y;
}
float height = Mathf.InverseLerp(minterainheight, maxterainheight, 0.5f); //vertices[iloopedforvertecy].z
Debug.LogWarning(height);
colors[iloopedforvertecy] = gradient.Evaluate(height);
}
}
}
void UpdateMsh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.colors = colors;
}
void Update()
{
CreateShape();
UpdateMsh();
}
}
I know that my code is messy. Still new to coding and unity in general.
Oh PS. Can somebody please help me add a collider to code generated object as you can see in the code above?
In your for-loop you increase the value of iloopedforvertecy before you evaluate your gradient value.
Because of this the last value of iloopedforvertecy will be greater than the array length of your colors array.
Try to move the line which increases the value at the end of the loop
for(int x = 0; x <= xsize; x++)
{
float xCoord = (float)x / xsize * scale + xoffset;
float zCoord = (float)z / zsize * scale + zoffset;
float y = Mathf.PerlinNoise(xCoord * smooth, zCoord * smooth) * smoothfactor;
vertices[iloopedforvertecy] = new Vector3(x, y, z);
// iloopedforvertecy += 1; // HERE THE VALUE WAS INCREASED
if(y > maxterainheight)
{
maxterainheight = y;
}
if(y < minterainheight)
{
minterainheight = y;
}
float height = Mathf.InverseLerp(minterainheight, maxterainheight, 0.5f); //vertices[iloopedforvertecy].z
Debug.LogWarning(height);
colors[iloopedforvertecy] = gradient.Evaluate(height);
iloopedforvertecy += 1; // HERE SHOULD THE VALUE BE INCREASED
}
In the example here I commented the two lines.

How can I make a circle from grid of GameObjects?

What I am trying to achieve is something like this:
What I have so far is the edges for the circles.
I know this would involve a nested for loop. This is what I have so far:
public GameObject player;
private GameObject playerGrid;
public int numOfObjects;
private Vector3 centerPos;
public int size = 2;
public Vector2 speed = new Vector2(50, 50);
private float smoothTime = 0.25f;
void Start()
{
playerGrid = new GameObject();
centerPos = transform.position;
for (int i = 0; i < numOfObjects; i++)
{
float pointNum = (i * 1.0f) / numOfObjects;
float angle = pointNum * Mathf.PI * 2;
float r = size / 2 * (Mathf.PI);
float x = Mathf.Sin(angle) * r;
float y = Mathf.Cos(angle) * r;
Vector3 pointPos = new Vector3(x, y, 0) + centerPos;
GameObject obj = Instantiate(player, pointPos, Quaternion.identity);
obj.transform.SetParent(playerGrid.transform);
}
}
I am stuck on how to implement the conditional for the nested for loop. Also, I have trouble understanding the calculations of column positions in the nested for loop. I believe the conditional would be the start and end of I for that column or row: for(int j = i + 1; j < i - 1, j++)
For the col positions, I would think it would be incrementing the angle enough to give the square its space for that column: float x = (Mathf.Sin(angle) + somethingHere) * r;
I just not sure how to progress from here.
Here's a simple way to draw a circle:
public float circleRadius = 5f;
public float objectSize = 1f;
void OnDrawGizmos()
{
for (var x = -circleRadius; x <= circleRadius; x++)
{
for (var y = -circleRadius; y <= circleRadius; y++)
{
var pos = new Vector3(x, 0f, y);
if (pos.magnitude >= circleRadius) continue;
Gizmos.DrawSphere(pos * (objectSize * 2f), objectSize);
}
}
}

Clone an Instantiated Object many times Unity3D

I have a list of prefabs, the script below instantiate 10 of them randomly in the Y axis.
The instantiated prefabs are random and different, but I want them to be same (clones of one prefab), how can I do that?
Script:
public GameObject[] Bricks;
void Start () {
SpawnCubes();
}
void SpawnBricks(int numCubes = 10, float startY = 3, float delta = 1)
{
for (int i = 0; i < numCubes; ++i)
{
int Rand = Random.Range(0, Bricks.Length);
var Brick = Instantiate(Bricks[Rand], new Vector3(0, startY - (float)i * delta, 0), Quaternion.identity);
Brick.transform.parent = gameObject.transform;
}
}
}
void SpawnBricks(int numCubes = 10, float startY = 3, float delta = 1)
{
int Rand = Random.Range(0, Bricks.Length);
for (int i = 0; i < numCubes; ++i)
{
var Brick = Instantiate(Bricks[Rand], new Vector3(0, startY - (float)i * delta, 0), Quaternion.identity);
Brick.transform.parent = gameObject.transform;
}
}
Like This??

How can I set the newPositions distance to be shorter closer to the original squadMembers positions?

I have 3 formations:
In each formation I'm calculating new positions to move to and add them to the List newPositions.
private void FormationTriangle()
{
newpositions = new List<Vector3>();
int height = Mathf.CeilToInt((Mathf.Sqrt(8 * squadMembers.Count + 1f) - 1f) / 2);
int slots = (int)(height * (height + 1f) / 2f);
float verticalModifier = 1.25f; // * 1.25f to increase horizontal space
float horizontalModifier = 0.8f; // * 0.8f to decrease "vertical" space
float width = 0.5f * (height - 1f);
Vector3 startPos = new Vector3(width * horizontalModifier, 0f, (float)(height - 1f) * verticalModifier);
int finalRowCount = height - slots + squadMembers.Count;
for (int rowNum = 0; rowNum < height && newpositions.Count < squadMembers.Count; rowNum++)
{
for (int i = 0; i < rowNum + 1 && newpositions.Count < squadMembers.Count; i++)
{
float xOffset = 0f;
if (rowNum + 1 == height)
{
// If we're in the last row, stretch it ...
if (finalRowCount != 1)
{
// Unless there's only one item in the last row.
// If that's the case, leave it centered.
xOffset = Mathf.Lerp(
rowNum / 2f,
-rowNum / 2f,
i / (finalRowCount - 1f)
) * horizontalModifier;
}
}
else
{
xOffset = (i - rowNum / 2f) * horizontalModifier;
}
float yOffset = (float)rowNum * verticalModifier;
Vector3 position = new Vector3(
startPos.x + xOffset, 0f, startPos.y - yOffset);
newpositions.Add(position);
}
}
move = true;
formation = Formation.Square;
}
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
for (int i = 0; i < squadMembers.Count; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
//squadMembers[i].transform.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
squadMembers[i].transform.Rotate(new Vector3(0, -90, 0));
newpositions.Add(new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y));
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
private Vector3 FormationCirclePositionCalculation(Vector3 center, float radius, int index, float angleIncrement)
{
float ang = index * angleIncrement;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
private void FormationCircle()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Vector3 center = transform.position;
float radius = (float)circleRadius / 2;
float angleIncrement = 360 / (float)numberOfSquadMembers;
for (int i = 0; i < numberOfSquadMembers; i++)
{
Vector3 pos = FormationCirclePositionCalculation(center, radius, i, angleIncrement);
var rot = Quaternion.LookRotation(center - pos);
if (Terrain.activeTerrain == true)
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpositions.Add(pos);
quaternions.Add(rot);
}
move = true;
squareFormation = false;
formation = Formation.Triangle;
}
Then I'm moving the squadMembers to the newPositions:
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers[i].transform.LookAt(newpositions[i]);
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) > 30f)
{
if (randomSpeed == true)
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[i]);
}
else
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[0]);
}
}
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) < threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternion, rotateSpeed * Time.deltaTime);
}
else
{
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternions[i], rotateSpeed * Time.deltaTime);
}
}
}
}
But if I want that the squadMembers will not move at all but will change to the next formation on the same position they are ? Or maybe to move just a little bit for example to distance 20 or 5 or 30 or 300 ?
I tried to add a distance check for the move:
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) > 30f)
So they are changing to the next formation when the distance more then 30.
But then the formation is not looking good. If for example it's the square formation the formation after they finished moving looks like a wave.
Screenshot example:
Wave formation

Categories