I have this code :
public void BrowseColliderToCreateMesh (PolygonCollider2D polygonColliderAdded){
//browse all path from collider
pathCount=polygonColliderAdded.pathCount;
CombineInstance[] combine = new CombineInstance[pathCount];
for (int i = 0; i < pathCount; i++)
{
Vector2[] path = polygonColliderAdded.GetPath(i);
Polygon2D polygon = Polygon2D.Contour(path);
Triangulation2D triangulation = new Triangulation2D(polygon, 22.5f);
// build a mesh from triangles in a Triangulation2D instance
singleMesh = triangulation.Build();
combine[i].mesh = singleMesh;
}
testDelaunay.GetComponent<MeshFilter>().mesh = new Mesh;
testDelaunay.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
}
1- I have a list of point from a polygonCollider2D, divide in 3 :
2- I loop through these path to generate mesh with Delaunay.
For 1 mesh it work well, but I can't find a way to combine it.
Example from unity use some other children gameobject that I don't have...
Does someone has a solution ?
I finally find something which is not optimize but which work :
private Mesh CombineMeshes(List<Mesh> meshes)
{
var combine = new CombineInstance[meshes.Count];
for (int i = 0; i < meshes.Count; i++)
{
combine[i].mesh = meshes[i];
combine[i].transform = transform.localToWorldMatrix;
}
var mesh = new Mesh();
mesh.CombineMeshes(combine);
return mesh;
}
public void BrowseColliderToCreateMesh (PolygonCollider2D polygonColliderAdded){
pathCount=polygonColliderAdded.pathCount;
for (int i = 0; i < pathCount; i++)
{
if(i==0){
Vector2[] path = polygonColliderAdded.GetPath(i);
Polygon2D polygon = Polygon2D.Contour(path);
Triangulation2D triangulation = new Triangulation2D(polygon, 22.5f);
// build a mesh from triangles in a Triangulation2D instance
singleMesh = triangulation.Build();
}else if (i==1){
Vector2[] path = polygonColliderAdded.GetPath(i);
Polygon2D polygon = Polygon2D.Contour(path);
Triangulation2D triangulation = new Triangulation2D(polygon, 22.5f);
// build a mesh from triangles in a Triangulation2D instance
newMesh = triangulation.Build();
combineMesh=CombineMeshes(new List<Mesh> { newMesh, singleMesh });
}else if(i>1){
Vector2[] path = polygonColliderAdded.GetPath(i);
Polygon2D polygon = Polygon2D.Contour(path);
Triangulation2D triangulation = new Triangulation2D(polygon, 22.5f);
newMesh = triangulation.Build();
combineMesh=CombineMeshes(new List<Mesh> { newMesh, combineMesh });
}
}
GetComponent<MeshFilter>().mesh = combineMesh;
}
Related
error detail
this mesh is generated by my script.I want to generate a voronoi diagram and split a plane to three zones.And I drag a material to one of the zones to test the generated result.
using SharpVoronoiLib;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms.VisualStyles;
using UnityEngine;
public class Fortune : MonoBehaviour
{
List<VoronoiEdge> edges;
private void Awake()
{
List<VoronoiSite> sites = new List<VoronoiSite>
{
new VoronoiSite(22, 26),
new VoronoiSite(10, 40),
new VoronoiSite(38, 5)
};
edges = VoronoiPlane.TessellateOnce(
sites,
0, 0,
50, 50
);
//site represent a zone. ClockwisePoints represent some points,which makes up the zone.
for (int i = 0; i < sites.Count; i++)
{
var ver = new List<Vector3>();
var uv= new List<Vector2>();
foreach (var item in sites[i].ClockwisePoints)
{
float x = (float)item.X;
float y = (float)item.Y;
ver.Add(new Vector3(x, 0, y));
uv.Add(new Vector2(x/50, y/50));
}
ver.Reverse();
uv.Reverse();
List<int> tri = new List<int>();
for (int j = 1; j < ver.Count - 1; j++)
{
tri.Add(0);
tri.Add(j);
tri.Add(j + 1);
}
Mesh mesh = new Mesh();
mesh.vertices = ver.ToArray();
mesh.triangles = tri.ToArray();
mesh.uv=uv.ToArray();
var gameObject = Resources.Load<GameObject>("test");
MeshFilter meshfilter = gameObject.GetComponent<MeshFilter>();
meshfilter.mesh = mesh;
Instantiate(gameObject,Vector3.zero,Quaternion.identity);
}
}
Maybe the uv setting has error.But I don't know the reason.Thank for your reply.
After some try,I find if I delete height map in shader.The error has disappeared.I don't know reason.
shader windows
I'm making a game where world is made out of tiles and I have a problem with small spaces being between tiles.
Each tile is normal object with sprite renderer.
Tiles share one splited texture.
Image of the problem
I tried to lower 'pixels per unit' in texture proporties, but then tiles like water (which are semi-transparent) overlap.
Here's code for instantiating tiles:
foreach (Layer layer in level.layers)
{
foreach (SavedTile tile in layer.tiles)
{
GameObject newTile = Instantiate(prefabTile, transform);
newTile.transform.position = new Vector3(
newTile.transform.position.x,
newTile.transform.position.y,
newTile.transform.position.z
);
BoxCollider2D newTileCollider = newTile.GetComponent<BoxCollider2D>();
SpriteRenderer newTileRenderer = newTile.GetComponent<SpriteRenderer>();
newTile.transform.position = new Vector3(tile.position.x, tile.position.y);
newTile.transform.rotation = new Quaternion(0, tile.rotation, 0, 0);
if (tile.tile.hasGravity)
{
newTile.AddComponent<Rigidbody2D>();
}
newTileRenderer.sprite = tile.tile.states[tile.slectedState].Sprites[0];
newTileRenderer.color = new Color(1, 1, 1, tile.tile.states[tile.slectedState].opacity);
if (tile.tile.states[tile.slectedState].isColliding)
{
newTileCollider.size = tile.tile.states[tile.slectedState].collider.size;
newTileCollider.offset = tile.tile.states[tile.slectedState].collider.offset;
if (tile.tile.states[tile.slectedState].bouyancy > 0)
{
newTile.AddComponent<BuoyancyEffector2D>().density = tile.tile.states[tile.slectedState].bouyancy;
newTileCollider.usedByEffector = true;
newTileCollider.isTrigger = true;
}
} else
{
newTileCollider.enabled = false;
}
if (tile.tile.customScript != null)
{
Debug.Log(tile.tile.customScript.GetClass());
newTile.AddComponent(tile.tile.customScript.GetClass());
}
if (tile.tile.isOnTop)
{
newTileRenderer.sortingOrder = -10;
}
}
}
I am trying to bake blendshapes in Unity:
store the original mesh bindposes and boneWeights
call Mesh.BakeMesh to bake the mesh's blendshapes into a static mesh
add blendshapes back to the new mesh
finally replace the SkinnedMeshRenderer's sharedMesh
It works except a single bone is seemingly randomly rotated incorrectly. I don't modify any of the transforms or change any bone settings.
Why is my tail bone rotated?
After some testing I found it definitely happens when I call AddBlendShapeFrame.
Code:
void Bake() {
Debug.Log("Baking...");
Mesh newMesh = new Mesh();
var originalMesh = skinnedMeshRenderer.sharedMesh;
var originalBlendShapeDetails = new List<BlendShapeDetails>();
for (var i = 0; i < originalMesh.blendShapeCount; i++) {
Vector3[] deltaVertices = new Vector3[originalMesh.vertexCount];
Vector3[] deltaNormals = new Vector3[originalMesh.vertexCount];
Vector3[] deltaTangents = new Vector3[originalMesh.vertexCount];
originalMesh.GetBlendShapeFrameVertices(i, 0, deltaVertices, deltaNormals, deltaTangents);
originalBlendShapeDetails.Add(new BlendShapeDetails() {
name = originalMesh.GetBlendShapeName(i),
weight = skinnedMeshRenderer.GetBlendShapeWeight(i),
deltaVertices = deltaVertices,
deltaNormals = deltaNormals,
deltaTangents = deltaTangents,
});
}
var originalBindposes = originalMesh.bindposes;
var originalBoneWeights = originalMesh.boneWeights;
Vector3[] baseVertices = new Vector3[originalMesh.vertexCount];
for (var i = 0; i < baseVertices.Length; i++) {
baseVertices[i] = new Vector3(0, 0, 0);
}
skinnedMeshRenderer.BakeMesh(newMesh);
newMesh.bindposes = originalBindposes;
newMesh.boneWeights = originalBoneWeights;
int idx = 0;
// add blendshapes
foreach (BlendShapeDetails blendShapeDetails in originalBlendShapeDetails) {
newMesh.AddBlendShapeFrame(blendShapeDetails.name, 100, blendShapeDetails.deltaVertices, null, null);
}
// set blendshapes to original weights
for (var i = 0; i < newMesh.blendShapeCount; i++) {
string name = newMesh.GetBlendShapeName(i);
var result = originalBlendShapeDetails.Find(item => item.name == name);
skinnedMeshRenderer.SetBlendShapeWeight(i, result.weight);
}
// store
skinnedMeshRenderer.sharedMesh = newMesh;
Debug.Log("Bake complete");
}
I'm working on a unity project involving deformable terrain based on marching-cubes. It works by generating a map of density over the 3-dimensional coordinates of a terrain chunk and using that data to create a mesh representing the surface of the terrain. It has been working, however the process is very slow. I'm attempting to introduce multithreading to improve performance, but I've run into a problem that's left me scratching my head.
When I run CreateMeshData() and try to pass my density map terrainMap into the MarchCubeJob struct, it recognizes it as a reference type, not a value type. I've seemed to whittle down the errors to this one, but I've tried to introduce the data in every way I know how and I'm stumped. I thought passing a reference like this was supposed to create a copy of the data disconnected from the reference, but my understanding must be flawed. My goal is to pass each marchingcube cube into a job and have them run concurrently.
I'm brand new to multithreading, so I've probably made some newbie mistakes here and I'd appreciate if someone would help me out with a second look. Cheers!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;
using Unity.Collections;
using Unity.Burst;
public class Chunk
{
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
public GameObject chunkObject;
MeshFilter meshFilter;
MeshCollider meshCollider;
MeshRenderer meshRenderer;
Vector3Int chunkPosition;
public float[,,] terrainMap;
// Job system
NativeList<Vector3> marchVerts;
NativeList<Vector3> marchTris;
MarchCubeJob instanceMarchCube;
JobHandle instanceJobHandle;
int width { get { return Terrain_Data.chunkWidth;}}
int height { get { return Terrain_Data.chunkHeight;}}
static float terrainSurface { get { return Terrain_Data.terrainSurface;}}
public Chunk (Vector3Int _position){ // Constructor
chunkObject = new GameObject();
chunkObject.name = string.Format("Chunk x{0}, y{1}, z{2}", _position.x, _position.y, _position.z);
chunkPosition = _position;
chunkObject.transform.position = chunkPosition;
meshRenderer = chunkObject.AddComponent<MeshRenderer>();
meshFilter = chunkObject.AddComponent<MeshFilter>();
meshCollider = chunkObject.AddComponent<MeshCollider>();
chunkObject.transform.tag = "Terrain";
terrainMap = new float[width + 1, height + 1, width + 1]; // Weight of each point
meshRenderer.material = Resources.Load<Material>("Materials/Terrain");
// Generate chunk
PopulateTerrainMap();
CreateMeshData();
}
void PopulateTerrainMap(){
...
}
void CreateMeshData(){
ClearMeshData();
vertices = new List<Vector3>();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < width; z++) {
Debug.Log(x + ", " + y + ", " + z + ", begin");
Vector3Int position = new Vector3Int(x, y, z);
// Set up memory pointers
NativeList<Vector3> marchVerts = new NativeList<Vector3>(Allocator.TempJob);
NativeList<int> marchTris = new NativeList<int>(Allocator.TempJob);
NativeList<float> mapSample = new NativeList<float>(Allocator.TempJob);
// Split marchcube into jobs by cube
instanceMarchCube = new MarchCubeJob(){
position = position,
marchVerts = marchVerts,
marchTris = marchTris,
mapSample = terrainMap
};
// Run job for each cube in a chunk
instanceJobHandle = instanceMarchCube.Schedule();
instanceJobHandle.Complete();
// Copy data from job to mesh data
//instanceMarchCube.marchVerts.CopyTo(vertices);
vertices.AddRange(marchVerts);
triangles.AddRange(marchTris);
// Dispose of memory pointers
marchVerts.Dispose();
marchTris.Dispose();
mapSample.Dispose();
Debug.Log(x + ", " + y + ", " + z + ", end");
}
}
}
BuildMesh();
}
public void PlaceTerrain (Vector3 pos, int radius, float speed){
...
CreateMeshData();
}
public void RemoveTerrain (Vector3 pos, int radius, float speed){
...
CreateMeshData();
}
void ClearMeshData(){
vertices.Clear();
triangles.Clear();
}
void BuildMesh(){
Mesh mesh = new Mesh();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
meshFilter.mesh = mesh;
meshCollider.sharedMesh = mesh;
}
private void OnDestroy(){
marchVerts.Dispose();
marchTris.Dispose();
}
}
// Build a cube as a job
[BurstCompile]
public struct MarchCubeJob: IJob{
static float terrainSurface { get { return Terrain_Data.terrainSurface;}}
public Vector3Int position;
public NativeList<Vector3> marchVerts;
public NativeList<int> marchTris;
public float[,,] mapSample;
public void Execute(){
//Sample terrain values at each corner of cube
float[] cube = new float[8];
for (int i = 0; i < 8; i++){
cube[i] = SampleTerrain(position + Terrain_Data.CornerTable[i]);
}
int configIndex = GetCubeConfiguration(cube);
// If done (-1 means there are no more vertices)
if (configIndex == 0 || configIndex == 255){
return;
}
int edgeIndex = 0;
for (int i = 0; i < 5; i++){ // Triangles
for (int p = 0; p < 3; p++){ // Tri Vertices
int indice = Terrain_Data.TriangleTable[configIndex, edgeIndex];
if (indice == -1){
return;
}
// Get 2 points of edge
Vector3 vert1 = position + Terrain_Data.CornerTable[Terrain_Data.EdgeIndexes[indice, 0]];
Vector3 vert2 = position + Terrain_Data.CornerTable[Terrain_Data.EdgeIndexes[indice, 1]];
Vector3 vertPosition;
// Smooth terrain
// Sample terrain values at either end of current edge
float vert1Sample = cube[Terrain_Data.EdgeIndexes[indice, 0]];
float vert2Sample = cube[Terrain_Data.EdgeIndexes[indice, 1]];
// Calculate difference between terrain values
float difference = vert2Sample - vert1Sample;
if (difference == 0){
difference = terrainSurface;
}
else{
difference = (terrainSurface - vert1Sample) / difference;
}
vertPosition = vert1 + ((vert2 - vert1) * difference);
marchVerts.Add(vertPosition);
marchTris.Add(marchVerts.Length - 1);
edgeIndex++;
}
}
}
static int GetCubeConfiguration(float[] cube){
int configurationIndex = 0;
for (int i = 0; i < 8; i++){
if (cube[i] > terrainSurface){
configurationIndex |= 1 << i;
}
}
return configurationIndex;
}
public float SampleTerrain(Vector3Int point){
return mapSample[point.x, point.y, point.z];
}
}
I've been working on building a procedural generator for Unity, that takes in noise and uses it to build a height map.
So far, everything seems to work as long as I limit the size of the mesh to around 250x250. If I attempt to make a larger mesh, the script won't calculate it.
The puzzling thing is that I get no memory errors or anything of that nature. I've implemented a Regenerate button that allows me to generate a new mesh in Unity and as long as I remain in the range of 250x250 or less, it works fine. If I pick a larger value, the mesh simply remains unchanged.
How I calculate the Mesh:
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
[ExecuteInEditMode]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(Noise))]
public class CustomTerrain : MonoBehaviour {
//Note: sizeX*sizeZ determines mesh size.
public int sizeX = 4; //These values are public so they can be
public int sizeZ = 4; //modified in the Unity Editor.
public float noiseSize = 1.0f;
public float cellSize = 1.0f;
private string mPath = "Assets/Generated/";
Noise noise;
void Start() {
noise = GetComponent<Noise>();
this.LoadMesh();
if (!GetComponent<MeshFilter>().sharedMesh) {
this.Regenerate();
}
}
public void LoadMesh() {
Mesh mesh = Instantiate(AssetDatabase.LoadMainAssetAtPath(mPath + gameObject.name + ".asset") as Mesh);
if (mesh) {
GetComponent<MeshFilter>().mesh = mesh;
}
recalculateMeshCollider(mesh);
}
Vector3[] GenVertices() {
float x, z;
int w = (sizeX+1);
int l = (sizeZ+1);
Vector3[] vertices = new Vector3[w*l];
for (int gx = 0; gx < w; gx++) {
for (int gz = 0; gz < l; gz++) {
x = gx*cellSize;
z = gz*cellSize;
float height = (noiseSize * noise.Get(x,z));
vertices[gx*l+gz] = new Vector3(x, height, z);
}
}
return vertices;
}
int[] GenTriangles() {
int vertciesPerTriangle = 3;
int trianglesPerCell = 2;
int numberCells = sizeX * sizeZ;
int[] triangles = new int[vertciesPerTriangle * trianglesPerCell * numberCells];
int tIndeX = 0;
for (int cX = 0; cX < sizeX; cX++) {
for (int cZ = 0; cZ < sizeZ; cZ++) {
int n = cX*(sizeZ+1)+cZ;
triangles[tIndeX] = n;
triangles[tIndeX+1] = n+1;
triangles[tIndeX+2] = n+sizeZ+2;
triangles[tIndeX+3] = n;
triangles[tIndeX+4] = n+sizeZ+2;
triangles[tIndeX+5] = n+sizeZ+1;
tIndeX +=6;
}
}
return triangles;
}
Vector2[] GenUVs() {
int w = (sizeX + 1);
int l = (sizeZ + 1);
Vector2[] uvs = new Vector2[w * l];
for (int uX = 0; uX < w; uX++) {
for (int uZ = 0; uZ < l; uZ++) {
uvs[uX*l+uZ] = new Vector2((float)uX/sizeX, (float)uZ/sizeZ);
}
}
return uvs;
}
}
My Regenerate function:
public void Regenerate() {
noise.Init();
Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
if(!mesh) {
mesh = new Mesh();
GetComponent<MeshFilter>().sharedMesh = mesh;
}
mesh.vertices = GenVertices();
mesh.triangles = GenTriangles();
mesh.uv = GenUVs();
mesh.RecalculateNormals();
recalculateMeshCollider(mesh);
}
public void recalculateMeshCollider(Mesh mesh) {
if (GetComponent<MeshCollider>()) {
DestroyImmediate(GetComponent<MeshCollider>());
}
transform.gameObject.AddComponent<MeshCollider>();
transform.GetComponent<MeshCollider>().sharedMesh = mesh;
}
By "250x250" do you mean there's 62.400 triangles?
Unity has a vertex limit of 65535 count - just use more than one mesh, no trouble.