I'm trying to implement transform.Rotate on the y axis in Unity using rotation matrix, is it possible? I wrote this function but it doesn't seems to work, pos is the position of the object to rotate and theta is the angle
public static MyQuaternion Rotate(Vector3 pos, float theta)
{
pos = pos.normalized;
pos -= pos;
float[,] m = new float[3, 3];
m[0, 0] = Mathf.Cos(theta);
m[0, 1] = 0;
m[0, 2] = Mathf.Sin(theta);
m[1, 0] = 0;
m[1, 1] = 1;
m[1, 2] = 0;
m[2, 0] = -Mathf.Sin(theta);
m[2, 1] = 0;
m[2, 2] = Mathf.Cos(theta);
Vector3 newPos = new Vector3();
float temp;
for (int i = 0; i < 3; ++i)
{
temp = 0;
for (int j = 0; j < 3; ++j)
{
temp += m[i, j] * pos[j];
}
newPos[i] = temp;
}
newPos += pos;
return new MyQuaternion(newPos.x, newPos.y, newPos.z, 0);
}
Do you have any idea? Thanks
If you want to get deeply into rotating a vector by hand, you are sort of doing it all right, however I am not sure about the type MyQuaternion you are returning.
If you just want to rotate a vector by an arbitrary angle you can just use Unity's build in Quaternion class. The mutliplication by Vector3 is defined as rotation of that vector so you can just do
Quaternion q=Quaternion.Euler(theta,0,0); // or however you want to map it
Vector3 rotatedVector=q*inputVector;
return rotatedVector;
Rotation in unity is generally done via Quaternion multiplication. To apply it to a gameobject, simply multiply its transform by a rotation you want to perform.
transform.rotation*=Quaternion.Euler(0,0,theta);
Position of an object does not have any play in rotation.
Related
I am trying to draw on a texture but I don't know the math to convert my mouse position to the texture position.
Currently, the code will draw on the texture but the coordinates are very off.
If someone can help me with this I would appreciate it.
var mousePosition = Input.mousePosition;
var drawPosition = new Vector2(mousePosition.x, mousePosition.y);
for (int x = 0; x < brushSize; x++)
{
for (int y = 0; y < brushSize; y++)
{
m_Texture.SetPixel(x + (int)drawPosition.x, y + (int)drawPosition.y, Color.clear);
}
}
m_Texture.Apply();
Try this:
Vector2 pos;
void Update(){
RectTransformUtility.ScreenPointToLocalPointInRectangle(
parentCanvas.transform as RectTransform, Input.mousePosition,
parentCanvas.worldCamera,
out pos);
}
Assign the parentCanvas and use the pos.x and pos.y inside your for loops
I am trying to make a 3D-voxel game that uses the marching cubes algorithm to procedurally generate a game world. This is working fine so far, except that, on exactly perpendicular sides on the positive and negative x sides of a given perpendicular piece of the world/chunk mesh, it looks like the uv coordinates aren't quite right as it just displays a solid color instead of the texture. [![view of debug-chunks and the buggy sides][1]][1]
At <2.> you can see the chunk wall how it is supposed to look like and at <1.> you can see the weird bug. This ONLY occurs on exactly perpendicular x-side triangles! Those meshes in the image are debug-chunks to show the problem.
All the noise-to-terrain translation works fine and I don't get any bugs there. It's only the uvs that cause problems.
I am using the following code to populate the Mesh.vertices, Mesh.triangles and Mesh.uv:
void MakeChunkMeshData()
{
for (int x = 0; x < Variables.chunkSize.x - 1; x++)
{
for (int y = 0; y < Variables.chunkSize.y - 1; y++)
{
for (int z = 0; z < Variables.chunkSize.z - 1; z++)
{
byte[] cubeCornerSolidityValues = new byte[8]{
chunkMapSolidity[x, y, z],
chunkMapSolidity[x + 1, y, z],
chunkMapSolidity[x + 1, y + 1, z],
chunkMapSolidity[x, y + 1, z],
chunkMapSolidity[x, y, z + 1],
chunkMapSolidity[x + 1, y, z + 1],
chunkMapSolidity[x + 1, y + 1, z + 1],
chunkMapSolidity[x, y + 1, z + 1]
};
MarchOne(new Vector3Int(x, y, z), cubeCornerSolidityValues);
}
}
}
}
void DrawChunk()
{
chunkMesh.vertices = vertices.ToArray();
chunkMesh.triangles = triangles.ToArray();
chunkMesh.SetUVs(0, uvs.ToArray());
chunkMesh.RecalculateNormals();
}
void ClearMesh()
{
chunkMesh.Clear();
}
void ClearChunkMeshAndData()
{
chunkMesh.Clear();
uvs = new List<Vector2>();
vertices = new List<Vector3>();
triangles = new List<int>();
}
/// <summary>
/// cube contains bytes for each corner of the cube
/// </summary>
/// <param name="cube"></param>
/// <returns></returns>
int GetCubeConfiguration(byte[] cube)
{
int u = 0;
int result = 0;
for(int corner = 0; corner < 8; corner++)
{
if (cube[corner] < Variables.solidityThreshold)
{
u++;
result |= 1 << corner;
}
}
return result;
}
Vector2[] getUvsPerTriangle(byte[] voxelTypes)
{
int resId = voxelTypes[0];
if (voxelTypes[1] == voxelTypes[2])
resId = voxelTypes[1];
resId = 1;
Vector2 normalized = getUvCoordFromTextureIndex(resId) / Constants.TextureAtlasSizeTextures;
float textureLength = 1f/Constants.TextureAtlasSizeTextures;
Vector2[] result = new Vector2[3] {
normalized + new Vector2(1,0) * textureLength,
normalized + new Vector2(0,1) * textureLength,
normalized + new Vector2(1,1) * textureLength
};
//Debug.Log(result);
return result;
}
/// <summary>
/// returns the absolute x and y coordinates of the given texture in the atlas (example: [4, 1])
/// </summary>
/// <param name="textureIndex"></param>
/// <returns></returns>
Vector2 getUvCoordFromTextureIndex(int textureIndex)
{
int x = textureIndex % Constants.TextureAtlasSizeTextures;
int y = (textureIndex - x) / Constants.TextureAtlasSizeTextures;
return new Vector2(x, y);
}
/// <summary>
/// takes the chunk-wide mesh data and adds its results after marching one cube to it.
/// </summary>
/// <returns></returns>
void MarchOne(Vector3Int offset, byte[] cube)
{
int configuration = GetCubeConfiguration(cube);
byte[] voxelTypes = new byte[3];
int edge = 0;
for (int i = 0; i < 5; i++) //loop at max 5 times (max number of triangles in one cube config)
{
for(int v = 0; v < 3; v++) // loop 3 times through shit(count of vertices in a TRIangle, who would have thought...)
{
int cornerIndex = VoxelData.TriangleTable[configuration, edge];
if (cornerIndex == -1) // indicates the end of the list of vertices/triangles
return;
Vector3 vertex1 = lwTo.Vec3(VoxelData.EdgeTable[cornerIndex, 0]) + offset;
Vector3 vertex2 = lwTo.Vec3(VoxelData.EdgeTable[cornerIndex, 1]) + offset;
Vector3Int vertexIndex1 = lwTo.Vec3Int(VoxelData.EdgeTable[cornerIndex, 0]) + offset;
Vector3Int vertexIndex2 = lwTo.Vec3Int(VoxelData.EdgeTable[cornerIndex, 1]) + offset;
Vector3 vertexPosition;
if (Variables.badGraphics)
{
vertexPosition = (vertex1 + vertex2) / 2f;
}
else
{
// currently using this "profile"
// this code determines the position of the vertices per triangle based on the value in chunkSolidityMap[,,]
float vert1Solidity = chunkMapSolidity[vertexIndex1.x, vertexIndex1.y, vertexIndex1.z];
float vert2Solidity = chunkMapSolidity[vertexIndex2.x, vertexIndex2.y, vertexIndex2.z];
float difference = vert2Solidity - vert1Solidity;
difference = (Variables.solidityThreshold - vert1Solidity) / difference;
vertexPosition = vertex1 + ((vertex2 - vertex1) * difference);
}
vertices.Add(vertexPosition);
triangles.Add(vertices.Count - 1);
voxelTypes[v] = chunkMapVoxelTypes[vertexIndex1.x, vertexIndex1.y, vertexIndex1.z];
edge++;
}
uvs.AddRange(getUvsPerTriangle(voxelTypes));
}
}
EDIT:
when only slightly rotating the chunks, the weird problem immediately disappears. I don't know why, but at least i now have some clue what's going on here.
[1]: https://i.stack.imgur.com/kYnkl.jpg
Well, it turns out that this probably is some weird behavior from unity. When explicitly setting the mesh after the mesh population process as the meshFilters' mesh, it works just fine. Also, I had to call mesh.RecalculateTangents() to make it work.
I am doing a Rubik cube generator with unity. Each of the pieces are basically a 1x1 cube which will be repeated in the shape of a bigger cube in my code as children of an empty object. The empty object is in the exact middle of the pieces, and all the pieces have their origins in the exact middle. However, when I put the empty to the center of the scene (0, 0, 0) It shows up in a different place.
Here are some pictures from the editor:
As you can see, the empty is in the center with coordinates set to 0, 0, 0
Now ,when it has children and the coordinates are all still 0, it shows in a different place
Edit:
#derHugo helped me out, but now my code that creates the cubes and sets the empty object to the middle of them does not work.
Here is the full code:
public GameObject PiecePrefab;
public int CubeSize;
Vector3 avg;
Vector3 ijk;
int cubeCount = 0;
// Start is called before the first frame update
void Start()
{
//Vector3 orgpos = gameObject.transform.position;
if (CubeSize <= 0)
{
CubeSize = 1;
Debug.LogError("The cube can not be smaller than 1!");
}
else if (CubeSize > 30)
{
CubeSize = 30;
Debug.LogError("The cube should not be bigger than 30!");
}
avg = new Vector3(0, 0, 0);
for (float k = 0; k < CubeSize; k++)
{
for (float j = 0; j < CubeSize; j++)
{
for (float i = 0; i < CubeSize; i++)
{
if (i == CubeSize - 1 || i == 0)
{
CreatePiece(i, j, k);
}
else if (j == CubeSize - 1 || j == 0)
{
CreatePiece(i, j, k);
}
else if (k == CubeSize - 1 || k == 0)
{
CreatePiece(i, j, k);
}
}
}
}
avg /= cubeCount;
gameObject.transform.position = avg;
var _Go = GameObject.FindGameObjectsWithTag("KuutionPala");
foreach (GameObject KuutionPala in _Go)
{
KuutionPala.transform.SetParent(transform);
}
//gameObject.transform.localPosition = orgpos;
void CreatePiece(float x, float y, float z)
{
ijk = new Vector3(x, y, z);
avg += ijk;
cubeCount++;
Vector3 offset3D;
offset3D = new Vector3(x / CubeSize, y / CubeSize, z / CubeSize);
var Piece = Instantiate(PiecePrefab, offset3D, transform.rotation);
Piece.transform.localScale /= CubeSize;
//Debug.LogFormat("x:" + x);
//Debug.LogFormat("y:" + y);
//Debug.LogFormat("z:" + z);
}
}
}
I think the error is on this row:
gameObject.transform.position = avg;
(Sorry if bad code)
As said there are two pivot modes in Unity (see Positioning GameObjects → Gizmo handle position toggles)
Pivot: positions the Gizmo at the actual pivot point of the GameObject, as defined by the Transform component.
Center: positions the Gizmo at a (geometrical) center position based on the selected GameObjects.
Yours is set to Center so in order to change that click on the button that says Center.
Then to your code
You are currently just hoping/assuming that your parent is correctly placed on 0,0,0.
Then you spawn all tiles in a range from 0 to (CubeSize - 1)/2 and then want to shift the center back.
I would rather go the other way round and calculate the correct local offset beforehand and directly spawn the tiles as children of the root with the correct offset. Into positive and negative direction.
Step 1: What is that local position?
For figuring the general maths out just look at two examples.
Let's say you have 3 cubes with indices 0,1,2. They have extends of 1/3 so actually there positions would need to look like
-0.5 0 0.5
| . | . | . |
Let's say you have 4 cubes with indices 0,1,2,3 and extends 1/4 then the positions would need to look like
-0.5 0 0.5
| . | . | . | . |
So as you can see the simplest way to go would be
start with the minimum position (e.g. -0.5f * Vector3.one)
always add half of the extends for the first offset (e.g. 1/CubeSize * 0.5f * Vector3.one)
add an offsets of the extends multiplied by the indices on top (e.g. 1/CubeSize * new Vector3(x,y,z))
so together something like
// be sure to cast to float here otherwise you get rounded ints
var extends = 1 / (float)CubeSize;
var offset = (-0.5f + extends * 0.5f) * Vector3.one + extends * new Vector3(x,y,z);
Step 2: Directly spawn as children with correct offset
void CreatePiece(float x, float y, float z)
{
var extends = 1 / (float)CubeSize;
var offset = (-0.5f + extends * 0.5f) * Vector3.one + extends * new Vector3(x,y,z);
var Piece = Instantiate(PiecePrefab, transform, false);
// This basically equals doing something like
//var Piece = Instantiate(PiecePrefab, transform.position, transform.rotation, transform);
Piece.transform.localPosition = offset;
Piece.transform.localScale = extends * Vector3.one;
}
Then you can reduce your code to
// Use a range so you directly clamp the value in the Inspector
[Range(1,30)]
public int CubeSize = 3;
// Start is called before the first frame update
void Start()
{
UpdateTiles();
}
// Using this you can already test the method without entering playmode
// via the context menu of the component
[ContextMenu(nameof(UpdateTiles)])
public void UpdateTiles()
{
// Destroy current children before spawning the new ones
foreach(var child in GetComponentsInChildren<Transform>().Where(child => child != transform)
{
if(!child) continue;
if(Application.isPlaying)
{
Destroy(child.gameObject);
}
else
{
DestroyImmediate(child.gameObject);
}
}
if (CubeSize < 1)
{
CubeSize = 1;
Debug.LogError("The cube can not be smaller than 1!");
}
else if (CubeSize > 30)
{
CubeSize = 30;
Debug.LogError("The cube should not be bigger than 30!");
}
// For making things easier to read I would use x,y,z here as well ;)
for (float x = 0; x < CubeSize; x++)
{
for (float y = 0; y < CubeSize; y++)
{
for (float z = 0; z < CubeSize; z++)
{
if (x == CubeSize - 1 || x == 0)
{
CreatePiece(x, y, z);
}
else if (y == CubeSize - 1 || y == 0)
{
CreatePiece(x, y, z);
}
else if (z == CubeSize - 1 || z == 0)
{
CreatePiece(x, y, z);
}
}
}
}
}
private void CreatePiece(float x, float y, float z)
{
var extends = 1 / (float)CubeSize;
var offset = (-0.5f + extends * 0.5f) * Vector3.one + extends * new Vector3(x,y,z);
var Piece = Instantiate(PiecePrefab, transform, false);
Piece.transform.localPosition = offset;
Piece.transform.localScale = extends * Vector3.one;
}
I made a Noise class using the Perlin Noise from Unity like this:
public static float[,] GetNoise(Vector2Int initialOffset, float scale, float persistance, float lacunarity, int octaves)
{
float[,] noiseMap = new float[Chunk.width, Chunk.height];
float maxHeight = 0;
float minHeight = 0;
for (int y = 0; y < Chunk.height; y++)
{
for (int x = 0; x < Chunk.width; x++)
{
float amplitude = 1;
float frequency = 1;
float noiseHeight = 0;
for (int oc = 0; oc < octaves; oc++)
{
float coordX = (x + initialOffset.x) / scale * frequency;
float coordY = (y + initialOffset.y) / scale * frequency;
float perlin = Mathf.PerlinNoise(coordX, coordY) * 2 - 1;
noiseHeight += perlin * amplitude;
amplitude *= persistance;
frequency *= lacunarity;
}
if (noiseHeight < minHeight)
{
minHeight = noiseHeight;
}
if (noiseHeight > maxHeight)
{
maxHeight = noiseHeight;
}
noiseMap[x, y] = noiseHeight;
}
}
for (int y = 0; y < Chunk.height; y++)
{
for (int x = 0; x < Chunk.width; x++)
{
noiseMap[x, y] = Mathf.InverseLerp(minHeight, maxHeight, noiseMap[x, y]);
}
}
return noiseMap;
}
However this code is giving me repeating patterns like this:
What am I doing wrong? Or there is no way to get rid of the patterns?
I got it working, not very well, but working. The way I did was I generate the height map for every tile in the chunk, then I did some random placing of tiles, while having in account the height map. Something like this:
if (heightMap[x, y] < 0.3 && Random.value < 0.5)
// Add tile
This way I got this result:
EDIT:
Doing some more research about Perlin Noise I found out that it just doesn't like negative coords for some reason, so I did this way, hope this helps someone!
so .. fixed the negative coords like this:
//account for negatives (ex. -1 % 256 = -1, needs to loop around to 255)
if (noiseOffset.x < 0)
noiseOffset = new Vector2(noiseOffset.x + noiseRange.x, noiseOffset.y);
if (noiseOffset.y < 0)
noiseOffset = new Vector2(noiseOffset.x, noiseOffset.y + noiseRange.y);
i'm trying to get the tangent vector for each point on the circle , i tried to use the derivative for the circle equation, but the result looks off in the viewport, so i'm wondering if i can find some help here
the code
public void OnDrawGizmos(){
step = (360.0f * Mathf.Deg2Rad) / numberOfPoints ;
CreateVertices();
}
void CreateVertices()
{
Points.Clear();
Normals.Clear();
Tangents.Clear();
float dynamicAngle = 0.0f;
for (int i = 0; i <= numberOfPoints; i++)
{
Vector3 point;
point.x = transform.position.x + Radius * Mathf.Cos(dynamicAngle);
point.y = transform.position.y + Radius * Mathf.Sin(dynamicAngle);
point.z = transform.position.z;
dynamicAngle = dynamicAngle + step;
if (i >= 1)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(Points[i - 1], point);
Gizmos.color = Color.white;
}
Points.Add(point);
CalculateNormals(dynamicAngle ,point);
CalculateTangents(dynamicAngle,i , point);
}
}
void CalculateNormals(float dynamicAngle , Vector3 point)
{
Vector3 Normal = (point - transform.position).normalized;
Gizmos.color = Color.magenta;
Gizmos.DrawLine(Normal, point);
Gizmos.color = Color.white;
Normals.Add(Normal);
}
void CalculateTangents(float dynamicAngle,int i ,Vector3 point)
{
Vector3 tangent;
tangent = new Vector3(-Normals[i].y, Normals[i].x, 0);
tangent.Normalize();
Gizmos.color = Color.blue;
Gizmos.DrawLine( point, tangent);
Gizmos.color = Color.white;
Tangents.Add(tangent);
}
Blue is the tangents purple is the normals, as you can see they are not perpendicular:
To understand my issue better here is a gif from unity viewport:
Since you already calculated the normals you can use the cross product to get the corresponding tangents
Vector3 up = new Vector3(0, 0, 1); // up side of your circle
Vector3 tangent = Vector3.Cross(normal, up);
If you only need to use circles on a specific plane you can also use this simplification
Vector3 tangent = new Vector3(-normal.y, normal.x, 0);
Edit:
The normals and tangents are direction vectors. They point from point in the direction the normal / tangent whould point. To draw the tangent, you have to pass the correct start and end points of the line by using
Gizmos.DrawLine(point, point + tangent);
If you move the GameObject away from the origin you will notice that the normals also get deformed, this has the same reason.
You are using 2D parametric equations:
x = x0 + r*cos(a)
y = y0 + r*sin(a)
z = z0
a = <0,2*Pi>
Tangent is unit circle coordinate with center (0,0,0) but shifted by 90 degrees:
tx = cos(a (+/-) pi/4)
ty = sin(a (+/-) pi/4)
tz = 0
Similarly bi-tangent is:
bx = (+/-) cos(a)
by = (+/-) sin(a)
bz = 0
and finally normal is
nx = 0
ny = 0
nz = (+/-) 1
The signs depends on your coordinate system conventions and movement direction.