I`m helping my friend to make his study project, and for it we need a 3d frustum, with and ability to set the diameter of the upper round surface and the bottom one in runtime, could you please advice how can it be done? I am thinking of getting the array of edgex/vertices, that are connected with the center one on the top and on the bottom, and change their coords, maybe there is an easier way to do so?
For creating a cone frustum with dynamic sizes you can use the script from http://wiki.unity3d.com/index.php/ProceduralPrimitives#C.23_-_Cone
Since you create the mesh once at beginning later you know exactly which vertices are the ones for the top and bottom plane so you can easily change them dynamically afterwards.
Somewhat like e.g.
public class DynamicConicalFrustum : MonoBehaviour
{
[SerializeField] private MeshFilter meshFilter;
[SerializeField] private MeshCollider meshCollider;
[Header("Settings")]
[SerializeField] private float _height = 1f;
[SerializeField] private float _bottomRadius = .25f;
[SerializeField] private float _topRadius = .05f;
[SerializeField] private int nbSides = 18;
private Mesh mesh;
const float _2pi = Mathf.PI * 2f;
private Vector3[] vertices;
private void Awake()
{
if (!meshFilter && !TryGetComponent<MeshFilter>(out meshFilter))
{
meshFilter = gameObject.AddComponent<MeshFilter>();
}
if(!GetComponent<MeshRenderer>())
{
var mr = gameObject.AddComponent<MeshRenderer>();
mr.material = new Material(Shader.Find("Standard"));
}
if (!meshCollider)
meshCollider = GetComponent<MeshCollider>();
mesh = meshFilter.mesh;
if (!mesh)
{
mesh = new Mesh();
}
meshFilter.mesh = mesh;
if (meshCollider)
meshCollider.sharedMesh = mesh;
RecreateFrustum(_height,_bottomRadius,_topRadius);
}
#if UNITY_EDITOR
private void OnValidate()
{
if (Application.isPlaying)
{
Awake();
}
}
#endif
public void RecreateFrustum(float height, float bottomRadius, float topRadius)
{
mesh.Clear();
int nbVerticesCap = nbSides + 1;
#region Vertices
// bottom + top + sides
vertices = new Vector3[nbVerticesCap + nbVerticesCap + nbSides * 2 + 2];
// Bottom cap
vertices[0] = new Vector3(0f, 0f, 0f);
for(var idx = 1; idx <= nbSides; idx++)
{
float rad = (float)(idx ) / nbSides * _2pi;
vertices[idx ] = new Vector3(Mathf.Cos(rad) * bottomRadius, 0f, Mathf.Sin(rad) * bottomRadius);
}
// Top cap
vertices[nbSides + 1] = new Vector3(0f, height, 0f);
for(var idx = nbSides + 2; idx <= nbSides * 2 + 1; idx++)
{
float rad = (float)(idx - nbSides - 1) / nbSides * _2pi;
vertices[idx] = new Vector3(Mathf.Cos(rad) * topRadius, height, Mathf.Sin(rad) * topRadius);
}
// Sides
int v = 0;
for(var idx = nbSides * 2 + 2; idx <= vertices.Length - 4; idx+=2)
{
float rad = (float)v / nbSides * _2pi;
vertices[idx] = new Vector3(Mathf.Cos(rad) * topRadius, height, Mathf.Sin(rad) * topRadius);
vertices[idx + 1] = new Vector3(Mathf.Cos(rad) * bottomRadius, 0, Mathf.Sin(rad) * bottomRadius);
v++;
}
vertices[vertices.Length - 2] = vertices[nbSides * 2 + 2];
vertices[vertices.Length - 1] = vertices[nbSides * 2 + 3];
#endregion
#region Triangles
int nbTriangles = nbSides + nbSides + nbSides * 2;
int[] triangles = new int[nbTriangles * 3 + 3];
// Bottom cap
int tri = 0;
int i = 0;
while (tri < nbSides - 1)
{
triangles[i] = 0;
triangles[i + 1] = tri + 1;
triangles[i + 2] = tri + 2;
tri++;
i += 3;
}
triangles[i] = 0;
triangles[i + 1] = tri + 1;
triangles[i + 2] = 1;
tri++;
i += 3;
// Top cap
//tri++;
while (tri < nbSides * 2)
{
triangles[i] = tri + 2;
triangles[i + 1] = tri + 1;
triangles[i + 2] = nbVerticesCap;
tri++;
i += 3;
}
triangles[i] = nbVerticesCap + 1;
triangles[i + 1] = tri + 1;
triangles[i + 2] = nbVerticesCap;
tri++;
i += 3;
tri++;
// Sides
while (tri <= nbTriangles)
{
triangles[i] = tri + 2;
triangles[i + 1] = tri + 1;
triangles[i + 2] = tri + 0;
tri++;
i += 3;
triangles[i] = tri + 1;
triangles[i + 1] = tri + 2;
triangles[i + 2] = tri + 0;
tri++;
i += 3;
}
#endregion
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
mesh.RecalculateTangents();
mesh.Optimize();
}
}
It's of course not really optimized currently but I hope you can keep going from there ;)
E.g. that method creates extra vertices for the sides .. one could re-use the ones from the top and bottom cap since they match positions anyway.
Further you could of course also just update the according vertices when the height and radius change, you wouldn't need to recreate the entire mesh every time since the triangles stay the same, only the vertices change positions.
this is the part of the code that generate a mesh, if you want more information ask.
So in the scene I cannot see it, I tried to enter play mode but nothing, if I go in the inspector I can see: 4 verts, 2 tris, the right amount of tris and verts.
public void ConstructMesh()
{
Vector3[] vertices = new Vector3[(resolution + 2) * (resolution + 2)];
int[] triangles = new int[(resolution + 1) * (resolution + 1) * 6];
int triIndex = 0;
for (int y = 0; y < resolution; y++)
{
for (int x = 0; x < resolution; x++)
{
int i = x + y * resolution;
Vector2 percent = new Vector2(x, y) / (resolution - 1);
Vector3 pointOnUnitCode = localUp + (percent.x - .5f) * 2 * axisA + (percent.y - .5f) * 2 * axisB;
vertices[i] = pointOnUnitCode;
triangles[triIndex] = i;
triangles[triIndex + 1] = i + resolution + 1;
triangles[triIndex + 2] = i + resolution;
triangles[triIndex + 3] = i;
triangles[triIndex + 4] = i + 1;
triangles[triIndex + 5] = i + resolution + 1;
triIndex += 6;
}
}
planet.Clear();
planet.vertices = vertices;
planet.triangles = triangles;
planet.RecalculateNormals();
}
You need a MeshRenderer and MeshFilter in the GameObject to be able to render your mesh.
Supposing your method ConstructMesh is a MonoBehaviour and it's attached to the same object you want to display the mesh, you can do the following:
GetComponent<MeshFilter>().mesh = planet;
So for some reason, this map generation works for up to xSize=100 and zSize=100.
As soon as I go further it does weird things like this (at xSize=300 and zSize=300):
Going even more up to 1000x1000 it generates something like this:
I have no idea why it works just up to 100x100.
Is there some mesh limitation I'm not aware of?
I first thought just the vertices weren't connecting properly but then after doing the 1000x1000 example and looking the 300x300 example from top it definitely doesn't even make the vertices in right place...
I can't see an error in my code for some reason. I'm stuck here looking the code blindly all day.
Any help?
Code [Just the CreateMapXZ() function]:
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
uvs = new Vector2[(xSize + 1) * (zSize + 1)];
float uvFactorX = 1.0f / xSize;
float uvFactorZ = 1.0f / zSize;
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
vertices[i] = new Vector3(x, 0, z);
uvs[i++] = new Vector2(x * uvFactorX, z * uvFactorZ);
//i++;
}
}
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++;
}
applyNoise();
UpdateMesh();
I have a normal class with no errors, but visual studio thinks the class ends before it should. It thinks the c* bracket of the whole class itself. Can you help me? I will show you a photo of what I mean:
You can see what I mean with the picture: the class ends where it shouldn't. I have already tried erasing a adding new brackets, copy and paste, and almost anything you can think of.
EDIT:
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using LukeWaffel.BUI;
using UnityEngine.UI;
public class Pattern : MonoBehaviour
{
public Text levelTxt;
public GameObject[] displayRow;
//a messageBox instance that can be used many times
private UIBox box;
public GameObject displayPrefab;
public GameObject displayBlockParent;
public GameObject userPrefab;
public GameObject userBlockParent;
public GameObject[] userRow;
//an array that is used for a function to check if all boolians in the array are true
private bool[] alltrue;
public int zNum;
public int xNum;
public int yNum;
private Color[] colors;
private bool isHardGenerate = false;
//the space between blocks needed to create a table
private float offset;
//how many colors are currently at play for exapmle: colorNum = 2 (the colors allowed in a pattern are red and yellow)
private int colorNum = 2;
// a variable that indicates the amount of colored blocks in a pattern
private int numToColor = 0;
public static int level = 0;
// a class that indicates which blocks are active: user or display;
private BlockController b;
// Start is called before the first frame update
private void Start()
{
levelTxt.text = "Level: " + (level + 1);
//initialize the colors available in the current game.
List<Color> colorsTemp = new List<Color>();
colorsTemp.Add(Color.red);
colorsTemp.Add(Color.yellow);
colorsTemp.Add(Color.green);
//convert the colors to array. the list was there just to add the colors hence thte name colorsTemp
colors = colorsTemp.ToArray<Color>();
//the blockController class controls how much time the user has to see the pattern before it is drawn
b = gameObject.GetComponent<BlockController>();
offset = 1f;
//start to generate the pattern
GenerateEasy();
}
private void GenerateEasy()
{
//set the number of colored blocks(randomized) GetRandomFloat is a custom function created to decide the skip of the randomized number. By defult that value is 0.5f
private float rnd = GetRandomFloat(1, 4);
if(rnd == 1)//that means that all blocks would be colored and thats an easy pattern. because of that we need to change the number
{
rnd++;
}
//the formula to decide how many blocks will be colored
numToColor = Mathf.CeilToInt((Num* xNum * yNum)/rnd);
// Create empty grid
displayRow = new GameObject[zNum * xNum * yNum];
userRow = new GameObject[zNum * xNum * yNum];
alltrue = new bool[yNum * xNum * zNum];
// Create blocks
for (int i = 0; i<yNum; i++)
{
for (int j = 0; j<xNum; j++)
{
for (int k = 0; k<zNum; k++)
{
//creating display table we are not selecting the pattern yet.
GameObject g = Instantiate(displayPrefab, new Vector3(j * offset, i * offset, k * offset), Quaternion.Euler(180, 0, 0), displayBlockParent.transform);
displayRow[i * (xNum * zNum) + j * zNum + k] = g;
//creating the coloring table (in all variables named build/user)
GameObject o = Instantiate(userPrefab, new Vector3(j * offset, i * offset, k * offset), Quaternion.Euler(180, 0, 0), userBlockParent.transform);
userRow[i * (xNum * zNum) + j * zNum + k] = o;
userRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color = colors[0];
}
}
}
//reverse the array because by defult the blocks are upside down, so we need to correct that and flip all the blocks again
Array.Reverse(displayRow);
Array.Reverse(userRow);
//create pattern for the display table. notice in the for loop the outer for loop runs numToColor times, the amount of blocks that should be colored
for (int s = 0; s<numToColor; s++)
{
//the for loop runs once too much, so in the first loop we need to not make any actions, unless num to color is 1, and then it only runs one time.
if (numToColor != 1 && s == 0)
{
continue;
}
//initialize the colored and notcolored lists. this only exists in generateEasy because this makes the pattern easier to remember. colored are the blocks that are not good for coloring and not colored are the blocks viable to color.
List<GameObject> colored = new List<GameObject>();
List<GameObject> notColored = new List<GameObject>();
//if non of the blocks are colored yet, color a block randomly
if (displayRow.ToList<GameObject>().All(p => p.GetComponent<Renderer>().material.color == colors[0]))
{
int randomIndex = UnityEngine.Random.Range(0, yNum * xNum * zNum);
displayRow[randomIndex].GetComponent<Renderer>().material.color = colors[UnityEngine.Random.Range(1, colorNum)];
}
//this for checks if any of the blocks that exist contain a color that isn't red(the defult color)
for (int i = 0; i<yNum; i++)
{
for (int j = 0; j<xNum; j++)
{
for (int k = 0; k<zNum; k++)
{
//the colors array are all the available colors in the game. because red is the defult color and the first one in the array, it does not count as a colored block and therefor need to be skipped for the if statment.
if (colors.Skip(1).Any(p => p == displayRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color))
{
//adding the colored blocks to the colored array, which helps later for the formula for the easy pattern, and at the same time making them unable to be recolored accidently by the pattern.
colored.Add(displayRow[i * (xNum * zNum) + j * zNum + k]);
}
}
}
}
//thia doe loop checks if the block that's not already colored is viable for being colored for the easy level. if it is, it adds the block to the notColored array
for (int i = 0; i<yNum; i++)
{
for (int j = 0; j<xNum; j++)
{
for (int k = 0; k<zNum; k++)
{
if (displayRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color == colors[0] && (colored.Any(p => (j != xNum - 1 && GameObject.ReferenceEquals(p, displayRow[i * (xNum * zNum) + (j + 1) * zNum + k]))
|| (j != 0 && GameObject.ReferenceEquals(p, displayRow[i * (xNum * zNum) + (j - 1) * zNum + k]))
|| (i != 0 && GameObject.ReferenceEquals(p, displayRow[(i - 1) * (xNum * zNum) + j * zNum + k]))
|| (i != yNum - 1 && GameObject.ReferenceEquals(p, displayRow[(i + 1) * (xNum * zNum) + j * zNum + k]))
|| (k != 0 && GameObject.ReferenceEquals(p, displayRow[i * (xNum * zNum) + j * zNum + k - 1]))
|| (k != zNum - 1 && GameObject.ReferenceEquals(p, displayRow[i * (xNum * zNum) + j * zNum + k + 1])))))
{
notColored.Add(displayRow[i * (xNum * zNum) + j * zNum + k]);
}
}
}
}
//finally, one of the not colored blocks in the not colored array is randomly colored with a random color according to level.
notColored.ToArray()[UnityEngine.Random.Range(0, notColored.ToArray().Length)].GetComponent<Renderer>().material.color = colors[UnityEngine.Random.Range(1, colorNum)];
}
}
private void GenerateHard()
{
//up until the very end, Generate hard is extremley similar to easyGenerate. if you want detailed comment on most of the function, please refer to easy generate.
//set the number of colored blocks
float rnd = GetRandomFloat(1, 4);
if (rnd == 1)
{
rnd++;
}
numToColor = Mathf.CeilToInt((zNum * xNum * yNum) / rnd);
displayRow = new GameObject[zNum * xNum * yNum];
userRow = new GameObject[zNum * xNum * yNum];
alltrue = new bool[yNum * xNum * zNum];
for (int i = 0; i < yNum; i++)
{
for (int j = 0; j < xNum; j++)
{
for (int k = 0; k < zNum; k++)
{
GameObject g = Instantiate(displayPrefab, new Vector3(j * offset, i * offset, k * offset), Quaternion.Euler(180, 0, 0), displayBlockParent.transform);
displayRow[i * (xNum * zNum) + j * zNum + k] = g;
GameObject o = Instantiate(userPrefab, new Vector3(j * offset, i * offset, k * offset), Quaternion.Euler(180, 0, 0), userBlockParent.transform);
userRow[i * (xNum * zNum) + j * zNum + k] = o;
userRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color = colors[0];
}
}
}
Array.Reverse(displayRow);
Array.Reverse(userRow);
//create a pattern. for now it is the same as easy generate but it's going to get different soon.
for (int s = 1; s < numToColor; s++)
{
List<GameObject> colored = new List<GameObject>();
List<GameObject> notColored = new List<GameObject>();
if (displayRow.ToList<GameObject>().All(p => p.GetComponent<Renderer>().material.color == colors[0]))
{
int randomIndex = UnityEngine.Random.Range(0, yNum * xNum * zNum);
displayRow[randomIndex].GetComponent<Renderer>().material.color = colors[UnityEngine.Random.Range(1, colorNum)];
}
for (int i = 0; i < yNum; i++)
{
for (int j = 0; j < xNum; j++)
{
for (int k = 0; k < zNum; k++)
{
if (colors.Skip(1).Any(p => p == displayRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color))
{
colored.Add(displayRow[i * (xNum * zNum) + j * zNum + k]);
}
}
}
}
//now is the different part, the for loop does not check if the block will be viable for an easy pattern, it just adds every blocks that's not colored, even if that block makes the pattern much harder
for (int i = 0; i < yNum; i++)
{
for (int j = 0; j < xNum; j++)
{
for (int k = 0; k < zNum; k++)
{
if (displayRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color == colors[0])
{
notColored.Add(displayRow[i * (xNum * zNum) + j * zNum + k]);
}
}
}
}
//like before, the progranm chooses to color a block in the not coloed array randomly.
notColored.ToArray()[UnityEngine.Random.Range(0, notColored.ToArray().Length)].GetComponent<Renderer>().material.color = colors[UnityEngine.Random.Range(1, colorNum)];
}
}
// Update is called once per frame
private void Update()
{
//this huge outer if is resposible for cheking the pattern
if (Input.GetKeyDown(KeyCode.Return) && !CrossHair.ok)
{
for (int i = 0; i < yNum; i++)
{
for (int j = 0; j < xNum; j++)
{
for (int k = 0; k < zNum; k++)
{
//checks if the pattern is exactly equal to what the user has colored
if (displayRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color.Equals(userRow[i * (xNum * zNum) + j * zNum + k].GetComponent<Renderer>().material.color))
{
alltrue[i * (xNum * zNum) + j * zNum + k] = true;
}
else
{
alltrue[i * (xNum * zNum) + j * zNum + k] = false;
}
}
}
}
//if the user got the pattern right then...
if (alltrue.ToList<bool>().All(b => b))
{
if ((level + 1) % 10 != 0 && level + 1 != 0)
{
//make the mouse appear and make the user unable to move the player or color any blocks so that the user can focus on the messegebox
CrossHair.ok = true;
Cursor.lockState = CursorLockMode.None;
Cursor.lockState = CursorLockMode.Confined;
Cursor.visible = true;
//display the message bix
box = new UIBox("bID", BUI.UIType.Message);
box.header = "You have passed the level!";
box.body = "You have passed this current level. Have you got what it takes to take on the next Puzzle?";
box.buttons.Add(new UIButton("Yes, next level!", FuncButton));
BUI.Instance.AddToQueue(box);
//reset everything for the next level. also increases the level.
b.Reset();
level++;
levelTxt.text = "Level: " + (level + 1);
DestroyArray(userRow);
DestroyArray(displayRow);
//generate hard and generate easy switch turns.
if (!isHardGenerate)
{
yNum++;
isHardGenerate = true;
GenerateHard();
}
else
{
isHardGenerate = false;
xNum++;
GenerateEasy();
}
}
//every 10 levels a color is added to the table.
else
{
Debug.Log("passed level 10");
CrossHair.ok = true;
Cursor.lockState = CursorLockMode.None;
Cursor.lockState = CursorLockMode.Confined;
Cursor.visible = true;
box = new UIBox("bID", BUI.UIType.Message);
box.header = "You have passed the level!";
box.body = "You have passed this current level. Have you got what it takes to take on the next Puzzle?";
box.buttons.Add(new UIButton("Yes, next level!", FuncButton));
BUI.Instance.AddToQueue(box);
xNum = 2;
yNum = 2;
colorNum++;
b.Reset();
level++;
levelTxt.text = "Level: " + (level + 1);
DestroyArray(userRow);
DestroyArray(displayRow);
isHardGenerate = false;
GenerateEasy();
}
}
//if the user lost then...
else
{
//for this refer to the beginning of the if statment
CrossHair.ok = true;
Cursor.lockState = CursorLockMode.None;
Cursor.lockState = CursorLockMode.Confined;
Cursor.visible = true;
box = new UIBox("bID", BUI.UIType.Message);
box.header = "You have failed";
box.body = "You have failed this current level. Have you got what it takes to try again?";
box.buttons.Add(new UIButton("Yes, try this level again!", FuncButton));
BUI.Instance.AddToQueue(box);
//if level doesnt equal to 0, go down a levle, you cant go down from the first level.
if (level != 0)
level--;
b.Reset();
levelTxt.text = "Level: " + (level + 1);
DestroyArray(userRow);
DestroyArray(displayRow);
if (isHardGenerate)
{
yNum--;
isHardGenerate = false;
GenerateEasy();
}
else
{
if (level != 0)
{
isHardGenerate = true;
xNum--;
GenerateHard();
}
else
{
GenerateEasy();
}
}
}
//destroys every onject in an array
void DestroyArray(GameObject[] b)
{
foreach (GameObject k in b)
Destroy(k.gameObject);
}
//lets the player move again and cloles the message box
void FuncButton(UIBox boxInfo, UIButton buttonInfo)
{
CrossHair.ok = false;
BUI.Instance.CloseBox(box.id);
}
}
}
//go to refrence to find the meaning of the function. (in easyGenerate.)
public float GetRandomFloat(int min, int max, float value = 0.5f)
{
int multipliedMin = (int)(min / value);
int multipliedMax = (int)(max / value);
return ((float)UnityEngine.Random.Range(multipliedMin, multipliedMax)) * value;
}
}
Remove private in private float rnd = GetRandomFloat(1, 4);
Take a look at the official documentation here in order to understand the access modifiers.
You're declaring a property inside a method. These should go within the initial class braces.
I'm using unity, c#, and I cannot figure out how I can generate a landscape with all different kinds of height colors. For instance, I want sand near the water and snow on mountain peaks. But I cannot manage to make this happen in code where the mesh is being generated.
Just to make clear, The mesh generates perfectly fine, but I cannot manage to change the colors of each separate triangle.
void CreateMesh()
{
planeMesh = new Mesh();
List<Vector3> vertices = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<int> indices = new List<int>();
List<Color> colors = new List<Color>();
for (int x = 0; x < gridSize + 1; x++)
{
for (int y = 0; y < gridSize + 1; y++)
{
float height = Mathf.PerlinNoise(y / (float)gridSize, x / (float)gridSize) * 15.0f;
vertices.Add(new Vector3(y, height, x));
if(height > 12)//mountain tops
{
uvs.Add(new Vector2(0, 1/3));//down to up, x first
colors.Add(Color.white);
}
else if(height > 3 && height < 13)//grassy fields
{
uvs.Add(new Vector2(1/3,2/3));
colors.Add(Color.green);
}
else if(height > 0 && height < 4)//sand
{
uvs.Add(new Vector2(2/3, 2/3));
colors.Add(Color.yellow);
}
}
}
//making the indices
for (int b = 0; b < gridSize; b++)
{
for (int c = 0; c < gridSize; c++)
{
indices.Add(b + ((gridSize + 1) * c));
indices.Add(b + gridSize + 1 + ((gridSize + 1) * c));
indices.Add(b + 1 + ((gridSize + 1) * c));
indices.Add(b + gridSize + 1 + ((gridSize + 1) * c));
indices.Add(b + gridSize + 2 + ((gridSize + 1) * c));
indices.Add(b + 1 + ((gridSize + 1) * c));
}
}
planeMesh.SetVertices(vertices);
planeMesh.SetIndices(indices.ToArray(), MeshTopology.Triangles, 0);
planeMesh.SetUVs(0, uvs);
planeMesh.RecalculateNormals();
planeMesh.colors = colors.ToArray();
GetComponent<MeshFilter>().mesh = planeMesh;
GetComponent<MeshFilter>().mesh.name = "Environment";
}
Could you guys help me with that?
Solved it! It seems the numbers I was using for the uvs were being rounded down to 0! I had to make them floats to use them properly.