WebGL : From World to Screen Coordinates - math

i have read all the posts about it, but cant get it done.
I have a 3d scene in webgl and need to know the 2d on the canvas for a 3d point.
I have this matrixes:
Perspective = M4x4.makePerspective( fovy, aspect, znear, zfar );
CameraView = M4x4.makeLookAt ( eye, center, up );
ViewPerspectiv = M4x4.mul( Perspective , CameraView );
And the model has:
this.ModelMatrix = M4x4.I;
this.ModelMatrix = M4x4.makeTranslate3( this.pos_x, this.pos_y, this.pos_z );
I have tryed all the posts - it always uses: view and Perspective and point .. and sometimes a transpose...
can someone use my varaiable names and just post me the lines ?? PLS !!
here is what i tryed .. but it did not work:
var point = get2dPoint( point, this.ModelMatrix, ViewPerspectiv , canvas.clientWidth, canvas.clientHeight ) ;
...
function get2dPointV2( point3D, viewMatrix, projectionMatrix, width, height)
{
var tmp1 = multiMat4ByVec3( viewMatrix , point3D );
var clip = multiMat4ByVec3( projectionMatrix , tmp1 );
var ndcSpace = new Array ( clip[0] / clip[3] , clip[1] / clip[3] , clip[2] / clip[3] );
ndcSpace[0] = ((ndcSpace[0] + 1.0) / 2.0) * width ;
ndcSpace[1] = ((ndcSpace[1] + 1.0) / 2.0) * height ;
return ndcSpace;
}
This is killing me for 12hours now..
Thanx to anybody who trys to help.
cu
uwi
i found out thet the basic approche was fine but the matrix i used needed to be transposed , that was all that was missing !
thanx anyway !

Your first sentence sounds like you want to project 3D -> 2D. Thats what the Perspective-Matrix does.
Your code is trying to invert the Perspective-Matrix, projecting from 2D -> 3D. This is used e.g. when trying to find out where you clicked with your mouse.
What are you trying to do? If you actually want to project 2D -> 3D i might find the time to explain/post some code tomorrow.

Related

Getting the bounding box or centers of models

I was wondering if there was a way to obtain the bounding box for the models that are inserted via 3dio.js, or otherwise calculate their center points? I'm looking to center them on the origin.
The images below show two models relative to the scene origin indicated by the red box.
You can access the three.js object of the 3d.io entity like this:
var threeElem = document.getElementById("custom-id").components['io3d-data3d'].data3dView.threeParent
Then you can use the native bounding box from three.js:
var bbox = new THREE.Box3().setFromObject(threeElem)
Like that you get the min/max bounds which you can use to determine the origin.
I hope that answers your question. Let me know!
Edit:
for furniture it would probably be
var threeElem = document.getElementById("custom-id").components['io3d-furniture'].data3dView.threeParent
Based on Madlaina's answer. I needed to ensure the model was loaded before
addModelToScene(type) {
let scene = document.querySelector('a-scene');
let model = document.createElement('a-entity');
model.setAttribute('io3d-data3d', getModelKey(type) )
model.addEventListener('model-loaded', () => {
// Access the three.js object of the 3d.io
let threeElem = model.components['io3d-data3d'].data3dView.threeParent
// create the bounding box
let bbox = new THREE.Box3().setFromObject(threeElem)
// Calculate the center-point offsets from the max and min points
const offsetX = (bbox.max.x + bbox.min.x)/2
const offsetY = (bbox.max.y + bbox.min.y)/2
const offsetZ = (bbox.max.z + bbox.min.z)/2
// apply the offset
model.setAttribute('position', {x:-offsetX,y:-offsetY, z:-offsetZ})
} );
scene.appendChild(model);
}
The result:

Three.js calculate object distance required to fill screen

I've seen lots of questions on how to zoom the camera so an object fills the screen, but I'm trying to move the object to fill the screen.
I've been trying something like this using the original photos pixel size, and these objects have been scaled:
var dist = object.originalSize.height > $(window).height()
|| object.originalSize.width > $(window).width()
? ( $(window).height() / object.originalSize.height ) * 100
: 10;
var pLocal = new THREE.Vector3( 0, 0, -dist);
var target = pLocal.applyMatrix4( camera.matrixWorld );
var tweenMove = new TWEEN.Tween(object.position).to(target, 1500).easing(TWEEN.Easing.Cubic.InOut);
To come up with a vector to move the object to, however, I can't get the object to fill the screen. Any idea of the maths I need to calculate how far an object needs to be to fill the screen?
The object is a Object3D with different children depending on it's type.
I know the original photographs dimensions (object.originalSize.height) and I know the geometry that has been scaled up to fit with power of 2.
Any clue gratefully received on how to calculate the distance required from the camera to ensure the object fits inside the bounds of the screen.
I also know the bounding box of the item, i.e. from 1024 to 128.
This works, not sure why..
var vFOV = camera.fov * Math.PI / 180;
var ratio = 2 * Math.tan( vFOV / 2 );
var screen = ratio * (window.innerWidth / window.innerHeight) ;
var size = getCompoundBoundingBox( object ).max.y;
var dist = (size/screen) / 4;

are AsciiEffect.js && CSS3DRenderer.js compatible? three.js / WebGL

I'm trying to use the AsciiEffect.js && the CSS3DRenderer.js in combination, but don't know if that's possible without some finagling... here's the idea
var W = window.innerWidth, H = window.innerHeight;
renderer = new THREE.CSS3DRenderer();
effect = new THREE.AsciiEffect( renderer );
effect.setSize( W, H );
document.body.appendChild( effect.domElement );
but that doesn't work... here's the problem, it's my understanding the AsciiEffect script uses the canvas renderer... I'm not entirely sure how the CSS3DRenderer works (beyond my skill set)... but in theory this seems like it should be possible... no? I assume it involves making some changes to either or...
No, they're not. AsciiEffect only works with canvas-based renderers: CanvasRenderer, SoftwareRenderer and WebGLRenderer.

Find which tiles are currently visible in the viewport of a Google Maps v3 map

I am trying to build support for tiled vector data into some of our Google Maps v3 web maps, and I'm having a hard time figuring out how to find out which 256 x 256 tiles are visible in the current map viewport. I know that the information needed to figure this out is available if you create a google.maps.ImageMapType like here: Replacing GTileLayer in Google Maps v3, with ImageMapType, Tile bounding box?, but I'm obviously not doing this to bring in traditional pre-rendered map tiles.
So, a two part question:
What is the best way to find out which tiles are visible in the current viewport?
Once I have this information, what is the best way to go about converting it into lat/lng bounding boxes that can be used to request the necessary data? I know I could store this information on the server, but if there is an easy way to convert on the client it would be nice.
Here's what I came up with, with help from the documentation (http://code.google.com/apis/maps/documentation/javascript/maptypes.html, especially the "Map Coordinates" section) and a number of different sources:
function loadData() {
var bounds = map.getBounds(),
boundingBoxes = [],
boundsNeLatLng = bounds.getNorthEast(),
boundsSwLatLng = bounds.getSouthWest(),
boundsNwLatLng = new google.maps.LatLng(boundsNeLatLng.lat(), boundsSwLatLng.lng()),
boundsSeLatLng = new google.maps.LatLng(boundsSwLatLng.lat(), boundsNeLatLng.lng()),
zoom = map.getZoom(),
tiles = [],
tileCoordinateNw = pointToTile(boundsNwLatLng, zoom),
tileCoordinateSe = pointToTile(boundsSeLatLng, zoom),
tileColumns = tileCoordinateSe.x - tileCoordinateNw.x + 1;
tileRows = tileCoordinateSe.y - tileCoordinateNw.y + 1;
zfactor = Math.pow(2, zoom),
minX = tileCoordinateNw.x,
minY = tileCoordinateNw.y;
while (tileRows--) {
while (tileColumns--) {
tiles.push({
x: minX + tileColumns,
y: minY
});
}
minY++;
tileColumns = tileCoordinateSe.x - tileCoordinateNw.x + 1;
}
$.each(tiles, function(i, v) {
boundingBoxes.push({
ne: projection.fromPointToLatLng(new google.maps.Point(v.x * 256 / zfactor, v.y * 256 / zfactor)),
sw: projection.fromPointToLatLng(new google.maps.Point((v.x + 1) * 256 / zfactor, (v.y + 1) * 256 / zfactor))
});
});
$.each(boundingBoxes, function(i, v) {
var poly = new google.maps.Polygon({
map: map,
paths: [
v.ne,
new google.maps.LatLng(v.sw.lat(), v.ne.lng()),
v.sw,
new google.maps.LatLng(v.ne.lat(), v.sw.lng())
]
});
polygons.push(poly);
});
}
function pointToTile(latLng, z) {
var projection = new MercatorProjection();
var worldCoordinate = projection.fromLatLngToPoint(latLng);
var pixelCoordinate = new google.maps.Point(worldCoordinate.x * Math.pow(2, z), worldCoordinate.y * Math.pow(2, z));
var tileCoordinate = new google.maps.Point(Math.floor(pixelCoordinate.x / MERCATOR_RANGE), Math.floor(pixelCoordinate.y / MERCATOR_RANGE));
return tileCoordinate;
};
An explanation: Basically, everytime the map is panned or zoomed, I call the loadData function. This function calculates which tiles are in the map view, then iterates through the tiles that are already loaded and deletes the ones that are no longer in the view (I took this portion of code out, so you won't see it above). I then use the LatLngBounds stored in the boundingBoxes array to request data from the server.
Hope this helps someone else...
For more recent users, it's possible to get tile images from the sample code in the documentation on this page of the Google Maps Javascript API documentation.
Showing Pixel and Tile Coordinates

How do I get the visual width and height of a rotated component?

I'm playing around with code like this:
<s:Button id="test" label="test" transformX="{Math.floor(test.width/2)}" rotationY="20" x="20" y="20" />
The button is rotated on the Y axis and the rotate pivot is in the middle of the button.
This will create a button that looks something like this:
(source: jeffryhouser.com)
The rotated button is, visually, filling a different space than the x, y, height, and width values would you have believe.
The "A" value in my image is the height of the button. But, what I want to use for calculation and placement purposes is the B value.
Additionally, I'd like to perform similar calculations with the width; getting the width from the top right corner to the bottom left corner.
How do I do this?
I put together a sample to show off the various approaches for calculating this that people are suggesting. The source code is also available. Nothing is quite working like I'd expect. For example, turn the rotationSlider to 85. The button is effectively invisible, yet all approaches are still giving it height and width.
My math may be a bit rusty, but this is how I would find the answer :
You would extend a right-triangle from the right edge of the button to the bottom-most point of the diagram you have (A-B). You can then use the Law of Sines to get three angles : 90', 20' and 70' (90 will always be there, and then your variable - 180 for the third angle).
You can then use the following formula to find your answer :
B = ((button.width * sin(button.rotationY)) / (sin(90 -button.rotationY)) + (button.height)
getBounds(..) and getRect(..) are supposed to be the methods for getting the width and height of transformed objects.
Not tried them in Flex 4 yet, but they always worked for me in Flex 3.
The answer was in one of the comments from James Ward on this question and is located at this blog post.
The one thing the blog post doesn't say is that in many cases, the perspectiveProjection property of the transform property on the class in question will be null. The linked to example took care of this by setting the maintainProjectionCenter property to true. But, you could also create a new perspectiveProjection object like this:
object.transform.perspectiveProjection = new PerspectiveProjection();
I wrapped up the function from evtimmy into a class:
/**
* DotComIt/Flextras
* Utils3D.as
* Utils3D
* jhouser
* Aug 5, 2010
*/
package com.flextras.coverflow
{
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Rectangle;
import flash.geom.Utils3D;
import flash.geom.Vector3D;
public class TransformUtilities
{
public function TransformUtilities()
{
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
//----------------------------------
// projectBounds
//----------------------------------
// info from
// http://evtimmy.com/2009/12/calculating-the-projected-bounds-using-utils3dprojectvector/
/**
* Method retrieved from
* http://evtimmy.com/2009/12/calculating-the-projected-bounds-using-utils3dprojectvector/
*
* #param bounds: The rectangle that makes up the object
* #param matrix The 3D Matrix of the item
* #param the projection of the item's parent.
*/
public static function projectBounds(bounds:Rectangle,
matrix:Matrix3D,
projection:PerspectiveProjection):Rectangle
{
// Setup the matrix
var centerX:Number = projection.projectionCenter.x;
var centerY:Number = projection.projectionCenter.y;
matrix.appendTranslation(-centerX, -centerY, projection.focalLength);
matrix.append(projection.toMatrix3D());
// Project the corner points
var pt1:Vector3D = new Vector3D(bounds.left, bounds.top, 0);
var pt2:Vector3D = new Vector3D(bounds.right, bounds.top, 0)
var pt3:Vector3D = new Vector3D(bounds.left, bounds.bottom, 0);
var pt4:Vector3D = new Vector3D(bounds.right, bounds.bottom, 0);
pt1 = Utils3D.projectVector(matrix, pt1);
pt2 = Utils3D.projectVector(matrix, pt2);
pt3 = Utils3D.projectVector(matrix, pt3);
pt4 = Utils3D.projectVector(matrix, pt4);
// Find the bounding box in 2D
var maxX:Number = Math.max(Math.max(pt1.x, pt2.x), Math.max(pt3.x, pt4.x));
var minX:Number = Math.min(Math.min(pt1.x, pt2.x), Math.min(pt3.x, pt4.x));
var maxY:Number = Math.max(Math.max(pt1.y, pt2.y), Math.max(pt3.y, pt4.y));
var minY:Number = Math.min(Math.min(pt1.y, pt2.y), Math.min(pt3.y, pt4.y));
// Add back the projection center
bounds.x = minX + centerX;
bounds.y = minY + centerY;
bounds.width = maxX - minX;
bounds.height = maxY - minY;
return bounds;
}
}
}
Although that is the answer to my question, I'm not sure if it was the solution to my problem. Thanks everyone!

Resources