I have atypical objects where I want to calculate its width, height and volume. For the sake of volume i have got this script
public class MeshVolume : MonoBehaviour
{
void Start()
{
Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
float volume = VolumeOfMesh(mesh);
string msg = "The volume of the mesh is " + volume + " cube units.";
Debug.Log(msg);
}
public float SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
{
float v321 = p3.x * p2.y * p1.z;
float v231 = p2.x * p3.y * p1.z;
float v312 = p3.x * p1.y * p2.z;
float v132 = p1.x * p3.y * p2.z;
float v213 = p2.x * p1.y * p3.z;
float v123 = p1.x * p2.y * p3.z;
return (1.0f / 6.0f) * (-v321 + v231 + v312 - v132 - v213 + v123);
}
public float VolumeOfMesh(Mesh mesh)
{
float volume = 0;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
for (int i = 0; i < mesh.triangles.Length; i += 3)
{
Vector3 p1 = vertices[triangles[i + 0]];
Vector3 p2 = vertices[triangles[i + 1]];
Vector3 p3 = vertices[triangles[i + 2]];
volume += SignedVolumeOfTriangle(p1, p2, p3);
}
return Mathf.Abs(volume);
}
}
As I am not a mathematican so I can't verify that its is working fine and not but one thing is clear that it is not updating the volume calculation if i scale up or down the object. So is this the correct way to calculate object volume?
Secondly, I am getting width and height of the object using meshRenderer bounds
Bounds meshBounds = GetComponent<MeshRenderer>().bounds;
float width = meshBounds.size.x;
float height = meshBounds.size.y;
Is this a correct approach for width and height calculation? How can I cross check it.
I have crossed check width and height and volume through revit tool.
For Width, Height and Breath::
Bounds meshBounds = GetComponent<MeshRenderer>().bounds;
float width = meshBounds.size.x;
float height = meshBounds.size.y;
float breath = meshBounds.size.z
In my case i have take MeshRendere bounds you can also calculate it through Collider.
For Volume:
public float SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
{
float v321 = p3.x * p2.y * p1.z;
float v231 = p2.x * p3.y * p1.z;
float v312 = p3.x * p1.y * p2.z;
float v132 = p1.x * p3.y * p2.z;
float v213 = p2.x * p1.y * p3.z;
float v123 = p1.x * p2.y * p3.z;
return (1.0f / 6.0f) * (-v321 + v231 + v312 - v132 - v213 + v123);
}
public float VolumeOfMesh(Mesh mesh)
{
float volume = 0;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
for (int i = 0; i < mesh.triangles.Length; i += 3)
{
Vector3 p1 = vertices[triangles[i + 0]];
Vector3 p2 = vertices[triangles[i + 1]];
Vector3 p3 = vertices[triangles[i + 2]];
volume += SignedVolumeOfTriangle(p1, p2, p3);
}
return Mathf.Abs(volume);
}
Related
I am currently creating a rubiks cube project. The cube solves, but now I'm trying to implement a 3d model of this cube.
At the moment the x axis and z axis rotations work correctly, but the y axis rotation seems to start of as a cube but as it rotates round becomes more of a trapezium as it rotates 180'.
I have this code:
Point3D final;
double x = rotation.x;
final.x = original.x;
final.y = original.y * Math.Cos(x) - original.z * Math.Sin(x);
final.z = original.y * Math.Sin(x) + original.z * Math.Cos(x);
original.x = final.x;
original.y = final.y;
original.z = final.z;
x = rotation.y;
final.x = original.z * Math.Sin(x) + original.x * Math.Cos(x);
final.y = original.y;
final.z = original.y * Math.Cos(x) - original.x * Math.Sin(x);
original.x = final.x;
original.y = final.y;
original.z = final.z;
x = rotation.z;
final.x = original.x * Math.Cos(x) - original.y * Math.Sin(x);
final.y = original.x * Math.Sin(x) + original.y * Math.Cos(x);
final.z = original.z;
typo. Change line for y-rotation to
final.z = original.z * Math.Cos(x) - original.x * Math.Sin(x);
You were using original.y instead of original.z, but for a y-rotation the value of y does not play into the rotation.
May I suggest you define the rotations in methods
public static class Rotations
{
public static Point3D RotateAboutX(this Point3D point, double angle)
{
return new Point3D(
point.X,
Math.Cos(angle) * point.Y- Math.Sin(angle) * point.Z,
Math.Sin(angle) * point.Y+ Math.Cos(angle) * point.Z);
}
public static Point3D RotateAboutY(this Point3D point, double angle)
{
return new Point3D(
Math.Cos(angle) * point.X + Math.Sin(angle) * point.Z,
point.Y,
-Math.Sin(angle) * point.X + Math.Cos(angle) * point.Z);
}
public static Point3D RotateAboutZ(this Point3D point, double angle)
{
return new Point3D(
Math.Cos(angle) * point.X - Math.Sin(angle) * point.Y,
Math.Sin(angle) * point.X + Math.Cos(angle) * point.Y,
point.Z);
}
}
and then used them as needed. For Example
Point3D final = original.RotateAboutX(rotation.x)
.RotateAboutY(rotation.y)
.RotateAboutZ(rotation.z);
or the remain true to the original code
Point3D final = original.RotateAboutX(rotation.x);
original = final;
final = original.RotateAboutY(rotation.y);
original = final;
final = original.RotateAboutZ(rotation.z);
I have seen many questions to conversions between Euler angles and Quaternion, but I never found any working solution. Maybe you can help me why this is not returning the right values. I need the conversion between Quaternions(XYZ) to Euler angles and this is the code I am currently using:
public static Vector3 Q2E(Quaternion q) // Returns the XYZ in ZXY
{
Vector3 angles;
angles.X = (float)Math.Atan2(2 * (q.W * q.X + q.Y * q.Z), 1 - 2 * (q.X * q.X + q.Y * q.Y));
if (Math.Abs(2 * (q.W * q.Y - q.Z * q.X)) >= 1) angles.Y = (float)Math.CopySign(Math.PI / 2, 2 * (q.W * q.Y - q.Z * q.X));
else angles.Y = (float)Math.Asin(2 * (q.W * q.Y - q.Z * q.X));
angles.Z = (float)Math.Atan2(2 * (q.W * q.Z + q.X * q.Y), 1 - 2 * (q.Y * q.Y + q.Z * q.Z));
return new Vector3()
{
X = (float)(180 / Math.PI) * angles.X,
Y = (float)(180 / Math.PI) * angles.Y,
Z = (float)(180 / Math.PI) * angles.Z
};
}
Thx everyone.
Your title is from Euler angles to Quaternions but you sample code is 'supposed' to convert from Quaternion to Euler.
Is below what you are looking for?
public class Program
{
public static void Main(string[] args)
{
EulerAngles e = new();
e.roll = 0.14;
e.pitch = 1.21;
e.yaw = 2.1;
// convert the Euler angles to Quaternions:
Quaternion q = ToQuaternion(e.yaw,e.pitch,e.roll);
// convert the same Quaternion back to Euler angles:
EulerAngles n = ToEulerAngles(q);
// verify conversion
Console.WriteLine($"Q: {q.x} {q.y} {q.z} {q.w}");
Console.WriteLine($"E: {n.roll} {n.pitch} {n.yaw}");
}
public class Quaternion
{
public double w;
public double x;
public double y;
public double z;
}
public class EulerAngles
{
public double roll; // x
public double pitch; // y
public double yaw; // z
}
public static Quaternion ToQuaternion(double yaw, double pitch, double roll)
{
double cy = Math.Cos(yaw * 0.5);
double sy = Math.Sin(yaw * 0.5);
double cp = Math.Cos(pitch * 0.5);
double sp = Math.Sin(pitch * 0.5);
double cr = Math.Cos(roll * 0.5);
double sr = Math.Sin(roll * 0.5);
Quaternion q = new Quaternion();
q.w = cr * cp * cy + sr * sp * sy;
q.x = sr * cp * cy - cr * sp * sy;
q.y = cr * sp * cy + sr * cp * sy;
q.z = cr * cp * sy - sr * sp * cy;
return q;
}
public static EulerAngles ToEulerAngles(Quaternion q)
{
EulerAngles angles = new();
// roll (x-axis rotation)
double sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
double cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
angles.roll = Math.Atan2(sinr_cosp, cosr_cosp);
// pitch (y-axis rotation)
double sinp = 2 * (q.w * q.y - q.z * q.x);
if (Math.Abs(sinp) >= 1)
{
angles.pitch = Math.CopySign(Math.PI / 2, sinp);
}
else
{
angles.pitch = Math.Asin(sinp);
}
// yaw (z-axis rotation)
double siny_cosp = 2 * (q.w * q.z + q.x * q.y);
double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
angles.yaw = Math.Atan2(siny_cosp, cosy_cosp);
return angles;
}
}
UPDATE: Using built-in classes for Quaternion and Euler Angles (Vector3):
using System.Numerics;
public static void Main()
{
Vector3 v = new() { X = 0.14F, Y = 1.21F, Z = 2.1F };
Quaternion q = ToQuaternion(v);
Vector3 n = ToEulerAngles(q);
Console.WriteLine($"Q: {q.X} {q.Y} {q.Z} {q.W}");
Console.WriteLine($"E: {n.X} {n.Y} {n.Z}");
}
public static Quaternion ToQuaternion(Vector3 v)
{
float cy = (float)Math.Cos(v.Z * 0.5);
float sy = (float)Math.Sin(v.Z * 0.5);
float cp = (float)Math.Cos(v.Y * 0.5);
float sp = (float)Math.Sin(v.Y * 0.5);
float cr = (float)Math.Cos(v.X * 0.5);
float sr = (float)Math.Sin(v.X * 0.5);
return new Quaternion
{
W = (cr * cp * cy + sr * sp * sy),
X = (sr * cp * cy - cr * sp * sy),
Y = (cr * sp * cy + sr * cp * sy),
Z = (cr * cp * sy - sr * sp * cy)
};
}
public static Vector3 ToEulerAngles(Quaternion q)
{
Vector3 angles = new();
// roll / x
double sinr_cosp = 2 * (q.W * q.X + q.Y * q.Z);
double cosr_cosp = 1 - 2 * (q.X * q.X + q.Y * q.Y);
angles.X = (float)Math.Atan2(sinr_cosp, cosr_cosp);
// pitch / y
double sinp = 2 * (q.W * q.Y - q.Z * q.X);
if (Math.Abs(sinp) >= 1)
{
angles.Y = (float)Math.CopySign(Math.PI / 2, sinp);
}
else
{
angles.Y = (float)Math.Asin(sinp);
}
// yaw / z
double siny_cosp = 2 * (q.W * q.Z + q.X * q.Y);
double cosy_cosp = 1 - 2 * (q.Y * q.Y + q.Z * q.Z);
angles.Z = (float)Math.Atan2(siny_cosp, cosy_cosp);
return angles;
}
How to calculate rotation matrix based on cone direction? I have a spotlight defined like this:
I'm creating vertices array for a cone to draw spot light shape and then I'm creating a buffer (line loop mode):
private static float[] createConeVerticesArray(float radius,
float height,
int segments)
{
Array<Float> verticesArray = new Array<>();
float angle = 2 * MathUtils.PI / segments;
float cos = MathUtils.cos(angle);
float sin = MathUtils.sin(angle);
float cx = radius, cy = 0;
for(int i = 0; i < segments; i++)
{
verticesArray.add(cx);
verticesArray.add(cy);
verticesArray.add(height);
verticesArray.add(0f);
verticesArray.add(0f);
verticesArray.add(0f);
verticesArray.add(cx);
verticesArray.add(cy);
verticesArray.add(height);
float temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
verticesArray.add(cx);
verticesArray.add(cy);
verticesArray.add(height);
}
verticesArray.add(cx);
verticesArray.add(cy);
verticesArray.add(height);
cx = radius;
cy = 0;
verticesArray.add(cx);
verticesArray.add(cy);
verticesArray.add(height);
float[] result = new float[verticesArray.size];
for(int i = 0; i < verticesArray.size; i++)
{
result[i] = verticesArray.get(i);
}
return result;
}
Next step I'm creating three spot lights like this which represents 3 rotations around each axis:
this.spotLights.add(new CSpotLight(
new Color(1f, 0f, 0f, 1f), //Color
new Vector3(0f, 0f, 5000f), //Position
1f, //Intensity
new Vector3(0f, 0f, -1f), //Direction
15f, //Inner angle
30f, //Outer angle
4000f, //Radius
1f)); //Attenuation
this.spotLights.add(new CSpotLight(
new Color(1f, 0f, 0f, 1f),
new Vector3(0f, 5000f, 0f),
1f,
new Vector3(0f, -1f, 0f),
15f,
30f,
4000f,
1f));
this.spotLights.add(new CSpotLight(
new Color(1f, 0f, 0f, 1f),
new Vector3(5000f, 0f, 0f),
1f,
new Vector3(-1f, 0f, 0f),
15f,
30f,
4000f,
1f));
And then I'm calculating quaternion for each spot light to create rotation matrix for model matrix (just for vertex shader purpose):
private static Quaternion quaternionLookRotation(Vector3 direction, Vector3 up)
{
Vector3 vector = Pools.obtain(Vector3.class).set(direction).nor();
Vector3 vector2 = Pools.obtain(Vector3.class).set(up).crs(vector).nor();
Vector3 vector3 = Pools.obtain(Vector3.class).set(vector).crs(vector2);
float m00 = vector2.x;
float m01 = vector2.y;
float m02 = vector2.z;
float m10 = vector3.x;
float m11 = vector3.y;
float m12 = vector3.z;
float m20 = vector.x;
float m21 = vector.y;
float m22 = vector.z;
Pools.free(vector);
Pools.free(vector2);
Pools.free(vector3);
float num8 = (m00 + m11) + m22;
Quaternion quaternion = Pools.obtain(Quaternion.class);
if (num8 > 0f)
{
float num = (float) Math.sqrt(num8 + 1f);
quaternion.w = num * 0.5f;
num = 0.5f / num;
quaternion.x = (m12 - m21) * num;
quaternion.y = (m20 - m02) * num;
quaternion.z = (m01 - m10) * num;
return quaternion;
}
if ((m00 >= m11) && (m00 >= m22))
{
float num7 = (float) Math.sqrt(((1f + m00) - m11) - m22);
float num4 = 0.5f / num7;
quaternion.x = 0.5f * num7;
quaternion.y = (m01 + m10) * num4;
quaternion.z = (m02 + m20) * num4;
quaternion.w = (m12 - m21) * num4;
return quaternion;
}
if (m11 > m22)
{
float num6 = (float) Math.sqrt(((1f + m11) - m00) - m22);
float num3 = 0.5f / num6;
quaternion.x = (m10+ m01) * num3;
quaternion.y = 0.5f * num6;
quaternion.z = (m21 + m12) * num3;
quaternion.w = (m20 - m02) * num3;
return quaternion;
}
float num5 = (float) Math.sqrt(((1f + m22) - m00) - m11);
float num2 = 0.5f / num5;
quaternion.x = (m20 + m02) * num2;
quaternion.y = (m21 + m12) * num2;
quaternion.z = 0.5f * num5;
quaternion.w = (m01 - m10) * num2;
return quaternion;
}
this.rotMatrix.set(quaternionLookRotation(
spotLight.getDirection(),
new Vector3(0, 1, 0)));
Two rotations from three works perfectly. The problem appears only in the third rotation axis which should faced point (0, 0, 0):
Here is my final code which works fine in any cases:
private static Quaternion rotationBetweenTwoVectors(Vector3 vec1, Vector3 vec2)
{
Quaternion result = Pools.obtain(Quaternion.class);
Vector3 u = Pools.obtain(Vector3.class).set(vec1).nor();
Vector3 v = Pools.obtain(Vector3.class).set(vec2).nor();
float dot = u.dot(v);
if(dot < -0.999999)
{
Vector3 tmp1 = Pools.obtain(Vector3.class).set(Vector3.X).crs(u);
if(tmp1.len() < 0.000001)
{
tmp1.set(Vector3.Y).crs(u);
}
tmp1.nor();
result.setFromAxisRad(tmp1, MathUtils.PI).nor();
Pools.free(tmp1);
}
else if(dot > 0.999999)
{
result.idt();
}
else
{
result.setFromCross(u, v);
}
Pools.free(u);
Pools.free(v);
return result;
}
Where setFromCross calculates axis from cross product of two vectors and angle like this:
final float dot = MathUtils.clamp(Vector3.dot(x1, y1, z1, x2, y2, z2), -1f, 1f);
final float angle = (float)Math.acos(dot);
Hope this will helps somebody :)
I am using the following code to calculate points for a circle arc drawn with a Line Renderer.
for (int i = 0; i <= pts; i++)
{
float x = center.x + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
float y = center.y + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
arcLine.positionCount = i + 1;
arcLine.SetPosition(i, new Vector2(x, y));
ang += (float)totalAngle / pts;
}
How can I change the angle ang to create a reflected arc along the line P1P2 as in the image below?
Please note that totalAngle represents the portion of the circle that is to be drawn between 0 and 360.
I'm not entirely sure that's possible, but I've come up with another way that works like this:
First, a helper function:
Vector2 GetPosition (float radius, float angle)
{
angle *= Mathf.Deg2Rad;
return new Vector2
{
x = radius * Mathf.Cos(angle),
y = radius * Mathf.Sin(angle)
};
}
Then, compute positions p1 and p2:
var p1 = GetPosition(radius, ang);
var p2 = GetPosition(radius, totalAngle);
To derive the mid-point p3:
var p3 = (p1 + p2) * 0.5f;
And finally rotate the original point about p3 to obtain the reflected point:
var pos = p3 * 2f - GetPosition(radius, ang);
And that's it! Your code should look something like this:
void Draw ()
{
var p1 = GetPosition(radius, ang);
var p2 = GetPosition(radius, totalAngle);
var p3 = (p1 + p2) * 0.5f;
for (int i = 0; i <= pts; i++)
{
var pos = p3 * 2f - GetPosition(radius, ang);
arcLine.positionCount = i + 1;
arcLine.SetPosition(i, center + pos);
ang += totalAngle / pts;
}
}
Vector2 GetPosition (float radius, float angle)
{
angle *= Mathf.Deg2Rad;
return new Vector2
{
x = radius * Mathf.Cos(angle),
y = radius * Mathf.Sin(angle)
};
}
Here's it in action:
I am trying to write an algorithm to convert my mouse click to 3D coordinates (to insert an object at this point).
I have "ground" level where Y = 0 and I want to calculate X and Z based on my mouse click. My function currently looks like that:
Point p = this.control.PointToClient(new Point(System.Windows.Forms.Cursor.Position.X, System.Windows.Forms.Cursor.Position.Y));
Vector3 pos = GeometryHelper.Unproject(new Vector3(p.X, 0f, p.Y), viewport.X, viewport.Y, viewport.Width, viewport.Height, projectionPlane.Near, projectionPlane.Far, Matrix4.Invert(mProjectionMatrix * camera.GetViewMatrix()));
active.applyGeometry(pos);
function applyGeometry simply sets the position of an object. I believe passed arguments are self-explanatory.
My Unproject function looks this way:
public static Vector3 Unproject(Vector3 vector, float x, float y, float width, float height, float minZ, float maxZ, Matrix4 inverseWorldViewProjection)
{
Vector4 result;
result.X = ((((vector.X - x) / width) * 2.0f) - 1.0f);
result.Y = ((((vector.Y - y) / height) * 2.0f) - 1.0f);
result.Z = (((vector.Z / (maxZ - minZ)) * 2.0f) - 1.0f);
result.X =
result.X * inverseWorldViewProjection.M11 +
result.Y * inverseWorldViewProjection.M21 +
result.Z * inverseWorldViewProjection.M31 +
inverseWorldViewProjection.M41;
result.Y =
result.X * inverseWorldViewProjection.M12 +
result.Y * inverseWorldViewProjection.M22 +
result.Z * inverseWorldViewProjection.M32 +
inverseWorldViewProjection.M42;
result.Z =
result.X * inverseWorldViewProjection.M13 +
result.Y * inverseWorldViewProjection.M23 +
result.Z * inverseWorldViewProjection.M33 +
inverseWorldViewProjection.M43;
result.W =
result.X * inverseWorldViewProjection.M14 +
result.Y * inverseWorldViewProjection.M24 +
result.Z * inverseWorldViewProjection.M34 +
inverseWorldViewProjection.M44;
result /= result.W;
return new Vector3(result.X, result.Y, result.Z);
}
The problem is that Unproject function returns result close to 0,0,0 and that makes my object appear around 0,0,0. Any idea how to modify it to work properly?
Update
I believe I have given enough details on this case, but in case you'd need something else to help me out just do not hesitate to tell me ; )