According to its description “Convert a point from 3D world space to 2D screen space.” I would expect to give the function a Vector3 representing the world position in 3D space, and receive in return a Vector3 representing the same position when projected into the 2D Screen space.
I checked the responses on that thread and it seems that the solution suggested in that case was to move the Text Element outside of the 2D Screen and into the 3D space?
I know that would work, but it means:
I have to rotate the Text Element to always face the camera
It’s not in the same hierarchy as the other UI elements, which makes it less clean
So, even though that option would work (and it is a valid solution) I’d really prefer to have a way to determine the projection of a 3D entity into the 2D Screen space, but my problem is I’m not sure how the engine is doing the scaling calculations to determine the 2D space.
Periodically encountered a similar need and finally decided to add my own method to camera component.
This works for any hierarchy of screen elements (sizes, anchors and pivots of parent elements or element itself don’t matter) and has been tested at different pixel ratios and screen resolutions.
Hope this helps someone.
/**@param {pc.Vec3} worldPos
* @param {pc.Vec3} screenPos
*/
pc.CameraComponent.prototype.worldToScreenSpace = function(worldPos, screenPos = undefined){
screenPos = this.worldToScreen(worldPos, screenPos);
var width = this.system.app.graphicsDevice.canvas.clientWidth;
var height = this.system.app.graphicsDevice.canvas.clientHeight;
screenPos.x = (screenPos.x / width) * 2 - 1; // 0 to 1 range value map to -1 to 1 range
screenPos.y = (1 - (screenPos.y / height)) * 2 - 1; // 1 to 0 range value map to -1 to 1 range
screenPos.z = 0;
return screenPos;
};
Usage example
/**@type {pc.CameraComponent} */
var camera; // camera reference
/**@type {pc.Entity} */
var worldTargetEntity; // reference to target world entity
/**@type {pc.Entity} */
var screenElementEntity; // reference to screen element entity you want to move
var screenPos = camera.worldToScreenSpace(worldTargetEntity.getPosition());
// // or if you have some 3D vector to receive screen coordinate result to avoid creating a new vector by worldToScreen method inside
// var exampleVec3 = new Vec3();
// var screenPos = camera.worldToScreenSpace(worldTargetEntity.getPosition(), exampleVec3);
// // And this does not create a new vector, it is simply more convenient for naming, so that in further operations the name is not some exampleVec3 or globalVec3, although it is a reference to that vector
screenElementEntity.setPosition(screenPos);