Unity3d - How to parent a worldspace GUI to a car? - user-interface

How can I attach a worldspace UI onto my car object? I'm using the UI Image rotational fill for a speedometer, how would I attach this image to my car? I've made it work somewhat by setting the position and rotation to that of the car, but it moves from it's position when turning, while still being rotated correctly. Help?
Code:
Vector3 basePos = relevantParent.transform.position;
Vector3 tweakedPos = basePos;
tweakedPos.x = tweakedPos.x + xOffset;
tweakedPos.y = tweakedPos.y + yOffset;
tweakedPos.z = tweakedPos.z + zOffset;
transform.position = tweakedPos;
transform.localRotation = relevantParent.transform.localRotation;

https://docs.unity3d.com/ScriptReference/RectTransform.html
RectTransform, which a world space canvas has as to know its position, rotation and so on, inherits from Transform.
Transform can set its parent object.
So grab your RectTransform from the WorldSpace canvas and set it's parent to be your cars transform.
RectTransform rectTransform;
rectTransform.parent = transform; //transform of your car.

Related

Oriented projectiles keep facing camera

I'm trying to render a 2d image that represent a projectile in a 3d world and i have difficulty to make the projectile face the camera without changing its direction. Im using JOML math library.
my working code to orient the projectile in his direction
public Quaternionf findRotation(Vector3f objectRay, Vector3f targetRay) {
Vector3f oppositeVector = new Vector3f(-objectRay.x, -objectRay.y, -objectRay.z);
// cas vecteur opposé
if(oppositeVector.x == targetRay.x && oppositeVector.y == targetRay.y && oppositeVector.z == targetRay.z) {
AxisAngle4f axis = new AxisAngle4f((float) Math.toRadians(180), 0, 0, 1);
Quaternionf result = new Quaternionf(axis);
return result;
}
objectRay = objectRay.normalize();
targetRay = targetRay.normalize();
double angleDif = Math.acos(new Vector3f(targetRay).dot(objectRay));
if (angleDif!=0) {
Vector3f orthoRay = new Vector3f(objectRay).cross(targetRay);
orthoRay = orthoRay.normalize();
AxisAngle4f deltaQ = new AxisAngle4f((float) angleDif, orthoRay.x, orthoRay.y, orthoRay.z);
Quaternionf result = new Quaternionf(deltaQ);
return result.normalize();
}
return new Quaternionf();
}
Now i want to add a vector3f cameraPosition parameter to rotate the projectile only on its x axis to face the camera but i dont know how to do it.
For example with this code the projectile correctly rotate around his x axis but not face the camera so i want to know how to find the correct angle.
this.lasers[i].getModel().rotate((float) Math.toRadians(5), 1, 0, 0);
I tried this to rotate around axis X with transforming vector before compute angle.
this.lasers[i] = new VisualEffect(this.position, new Vector3f(1,1,1), color, new Vector2f(0,0.33f));
this.lasers[i].setModel(new Matrix4f().scale(this.lasers[i].getScale()));
this.lasers[i].getModel().rotate(rotation);
this.lasers[i].getModel().translateLocal(this.lasers[i].getPosition());
Vector3f vec = new Vector3f(cameraPosition).sub(this.position);
Vector4f vecSpaceModel = this.lasers[i].getModel().transform(new Vector4f(vec, 1.0f));
Vector4f normalSpaceModel = this.lasers[i].getModel().transform(new Vector4f(normal, 1.0f));
float angleX = new Vector2f(vecSpaceModel.y, vecSpaceModel.z).angle(new Vector2f(normalSpaceModel.y, normalSpaceModel.z));
this.lasers[i].getModel().rotate(angleX, 1, 0, 0);
Since you are using JOML, you can massively simplify your whole setup.
Let's assume that:
projectilePosition is the position of the projectile,
targetPosition is the position the projectile is flying at/towards, and
cameraPosition is the position of the "camera" (which we ultimately want the projectile to face)
We will also assume that the local coordinate system of the projectile is such that its +X axis points along the projectile's path (like how you depicted it) and the +Z axis points away from the projectile towards the viewer when the viewer is "facing" the projectile. So, the projectile itself is defined as a quad on the XY plane within its own local coordinate system.
What we must do now is create a basis transformation that will effectively transform the projectile such that its X axis points towards the "target" and its Z axis points "as best as we can" towards the camera.
This is very reminiscent of what we know as the "lookAt" transformation in OpenGL. And in fact, we are just going to use that. However, since the common "lookAt" is the inverse of what we wanted to do, we will also just invert it.
So, all in all, your complete model matrix/transformation for a single projectile will look like this (in JOML):
Vector3f projectilePosition = ...;
Vector3f cameraPosition = ...;
Vector3f targetPosition = ...;
Vector3f projectileToCamera = new Vector3f(cameraPosition).sub(projectilePosition);
modelMatrix
.setLookAt(projectilePosition, targetPosition, projectileToCamera)
.invert()
.rotateXYZ((float) Math.toRadians(-90), 0, (float) Math.toRadians(90));
In case you do not want to use lookAt and invert, you can also do:
Vector3f projectileToTarget = new Vector3f(targetPosition).sub(projectilePosition);
modelMatrix
.translation(projectilePosition)
.rotateTowards(projectileToTarget, projectileToCamera)
.rotateXYZ((float) Math.toRadians(-90), 0, (float) Math.toRadians(-90));
yielding the same result as the above code.
Note that nowhere do we actually need angles or trigonometric functions. This is very common when you already have all positions/directions given as vectors, you can simply use linear algebra without converting from/to angles.
The last part with the rotateXYZ(90°, 0°, 90°) is to express that we do not want the -Z axis of the projectile to point towards the target (which is what lookAt will do by default), but we want the X axis to point to the target.
Yet another way is to realize that what we do here is also known as a "cylindrical" or "axial" billboard, and can also be expressed like so:
Vector3f projectileToTarget = new Vector3f(targetPosition).sub(quadPosition).normalize();
modelMatrix
.billboardCylindrical(projectilePosition, cameraPosition, projectileToTarget)
.rotateZ((float) Math.toRadians(90));
(Note that in this case projectileToTarget needs to be unit!)
A test with a simple scene containing 24 projectiles all targeting "the center" with the camera hovering over them will look like this:
The corresponding simple LWJGL 3 / JOML demo generating this image.

Three.js: How to rotate a Sphere on Axis using camera rotation values

I have a special control called SphericalControls. Its similar to OrbitControls, but it keeps camera at position 0,0,0 and instead rotates camera on x and y to look around a scene. It is placed in the middle of a SphereBufferGeometry which has a 360 equirectangular image projected upon it. The user can look around the 360 image, and as he does the camera x and y rotation values change.
When a user clicks a button, I need to take these x and y rotation values and rotate the sphere to the rotation of the camera. I then set camera back to x:0 and y:0.
The result is that the camera is reset and the 360 scene has now rotated to show the same rotation view that the camera was previously looking at. So to the user, the view stays basically static, just the values for camera.rotation and sphere rotation have swapped.
This works great if I offset the texture on the sphere:
sphereObj.material.map.wrapS = THREE.RepeatWrapping;
sphereObj.material.map.offset.x = ((camera.rotation.x) / (Math.PI * 2));
sphereObj.material.map.needsUpdate = true;
sphereObj.material.needsUpdate = true;
camera.rotation.set(0, 0);
// Success!
But what I need to do is not offset the texture, but rotate the entire geometry. I have tried:
var axis = new THREE.Vector3(0, 1, 0).normalize();;
var offsetRadian = ((camera.rotation.x) / (Math.PI * 2));
sphere.rotateOnAxis(axis, offsetRadian);
// Fail
But the result is that the sphere rotation is off by approx 30%. Any help is appreciated.
Every objects' rotational data is stored in their respective .quaternion object. Both camera and sphereObj have a quaternion, so what you could do is copy the camera's rotational data into the sphere:
// Get camera's rotation
targetRotation = camera.quaternion;
// Invert rotation
targetRotation.inverse();
// Set sphere's rotation
sphereObj.quaternion.copy(targetRotation);
camera.rotation.set(0, 0, 0);
I'm not entirely sure if you need the .inverse() line... if you're noticing the sphere is rotating in the opposite direction, just get rid of it to get the desired result.

ARCore : decompose view matrix

I'm trying to get eye coordinates (camera's position, direction and up) from pose's view matrix, but what I get is not what I expected.
First, my goal is an Yup coordinate system :
I'm not sure ARCore use the same system, I did not found precise informations about the used coordinate system.
Next, I'm decomposing the view matrix, but if the results are mathematically good (direction and up seems to be in the good directions, position seems to have the good scale) the result is very cahotic since my camera move strangely around my scene.
// Get camera matrix and draw.
float[] viewmtx = new float[16];
frame.getViewMatrix(viewmtx, 0);
Vector3 pos = new Vector3(viewmtx[12], viewmtx[13], viewmtx[14]);
Vector3 camDir = new Vector3(viewmtx[8], viewmtx[9], viewmtx[10]).nor().scl(-1);
Vector3 camUp = new Vector3(viewmtx[4], viewmtx[5], viewmtx[6]).nor();
Does something sound strange for you?
Here is the answer :
The device / configuration i'm using is in landscape mode, so the coordinate frame is not this one.
Here is my fix :
Vector3 pos = new Vector3(viewmtx[12], viewmtx[13], viewmtx[14]).scl(1000);
Vector3 camDir = new Vector3(viewmtx[8], viewmtx[9], viewmtx[10]).nor();
Vector3 camUp = new Vector3(viewmtx[4], viewmtx[5], viewmtx[6]).nor();
if (isLandscape) {
pos = new Vector3(viewmtx[12], viewmtx[13], viewmtx[14]).scl(1000);
camDir = new Vector3(-viewmtx[8], -viewmtx[9], -viewmtx[10]).nor();
camUp = new Vector3(viewmtx[0], viewmtx[1], viewmtx[2]).nor();
}

How to convert world rotation to screen rotation?

I need to convert the position and rotation on a 3d object to screen position and rotation. I can convert the position easily but not the rotation. I've attempted to convert the rotation of the camera but it does not match up.
Attached is an example plunkr & conversion code.
The white facebook button should line up with the red plane.
https://plnkr.co/edit/0MOKrc1lc2Bqw1MMZnZV?p=preview
function toScreenPosition(position, camera, width, height) {
var p = new THREE.Vector3(position.x, position.y, position.z);
var vector = p.project(camera);
vector.x = (vector.x + 1) / 2 * width;
vector.y = -(vector.y - 1) / 2 * height;
return vector;
}
function updateScreenElements() {
var btn = document.querySelector('#btn-share')
var pos = plane.getWorldPosition();
var vec = toScreenPosition(pos, camera, canvas.width, canvas.height);
var translate = "translate3d("+vec.x+"px,"+vec.y+"px,"+vec.z+"px)";
var euler = camera.getWorldRotation();
var rotate = "rotateX("+euler.x+"rad)"+
" rotateY("+(euler.y)+"rad)"+
" rotateY("+(euler.z)+"rad)";
btn.style.transform= translate+ " "+rotate;
}
... And a screenshot of the issue.
I would highly recommend not trying to match this to the camera space, but instead to apply the image as a texture map to the red plane, and then use a raycast to see whether a click goes over the plane. You'll save yourself headache in translating and rotating and then hiding the symbol when it's behind the cube, etc
check out the THREEjs examples to see how to use the Raycaster. It's a lot more flexible and easier than trying to do rotations and matching. Then whatever the 'btn' onclick function is, you just call when you detect a raycast collision with the plane

Feature projection into equirectangular panorama with THREE.js

Relatively new to THREE.js. I am trying to figure out how to project a DIV with text into an equirectangular panorama.
I have this simple example working with my panorama images.
https://threejs.org/examples/webgl_panorama_equirectangular
The question: I have a latitude and longitude of a feature that's in my panorama, I'd like to project a DIV labeling such item into 3D space. How do I convert longitude and latitude into X and Y on the canvas so I can change the DIVS left and top style attributes so the label renders in 3D space and appears fixed to its coordinates?
UPDATE:
For clarity, how does one take planet earth longitude and latitude, and convert it into X Y pixels inside a mesh? I know where the image was taken on earth, and I know where an item in that picture was taken on earth. I want to label that item in 3D space.
Any help would be much appreciated. Thanks.
Code from this question seems to do the trick:
3d coordinates to 2d screen position
function getCoordinates( element, camera ) {
var screenVector = new THREE.Vector3();
element.localToWorld( screenVector );
screenVector.project( camera );
var posx = Math.round(( screenVector.x + 1 ) * renderer.domElement.offsetWidth / 2 );
var posy = Math.round(( 1 - screenVector.y ) * renderer.domElement.offsetHeight / 2 );
console.log( posx, posy );
}
I updated the jsfiddle with the new version of Three.js
http://jsfiddle.net/L0rdzbej/409/

Resources