I have an array of quads, each having it’s own position, defines the coordinates of the vertices. I have scene's projection on the plane of the camera. I want to know projection area of each quad's array element.
In fact, i wont to know by projection pixel which object it belongs.
You can project each vertex onto the screen with the Viewport.Project method.
After that you have the quad's vertices in screen coordinates. All that remains is determining, of the point in question belongs to that polygon. This can be done in different ways.
One option is to determine the side of the point relative to each polygon edge. This works beacuse the polygon is convex.
previousSide := 0
for each edge in polygon
d1 := edge.p2 - edge.p1 //direction vector of edge
d2 := pointInQuestion - edge.p1 //direction of first edge point to point in question
side = d1.x * d2.y - d1.y * d2.x //cross product
if side * previousSide < 0 then return false //different sides
previousSide := side
next
return true
Related
I want to let the user control an object moving over the surface of a static sphere. Using two buttons to rotate the direction of the object clockwise and anti-clockwise as it constantly moves forward similar to asteroids.
In scene kit there are three different orientation properties for an SCNNode and I really don't know where to start. I understand how to execute everything except the rotation around the sphere.
You're looking for a parameterization of the surface of the sphere. You can find this online (but it can be tricky if you don't know the magic words to enter for your searches). Check out the entry on MathWorld.
The surface of the sphere is parameterized by two angle variables, call them s and t. Note that one variable will run from zero to 2 pi, and the other will run only from zero to pi. This is a gotcha that can be easy to miss. To convert these angles to rectangular (x, y, z) coordinates, you use the formula:
x = r cos(s) sin(t)
y = r sin(s) sin(t) // Yes it's sin(t) twice, that's not a typo.
z = r cos(t)
I find the following visualization helpful. A curve in a plane (the xy-plane, for example) sweeps out an angle from zero to pi, half a rotation and corresponds to the parameter s. If you set t equal to pi/2, so sin(t) = 1, then you can see how x and y turn into standard rectangular coordinates for a circular section. After the s parameter sweeps out half a circle, you can rotate that half circle all the way around from zero to 2 pi, to form a full sphere, and that full sweep corresponds to the parameter t.
If you represent your object's position by coordinates (s, t) then you can, for the most part, safely convert to rectangular coordinates using the formula above without worrying about the domain of either parameter; however if s or t grow without bound (say, because your object orbits continuously for a long time) it might be worth the small extra effort to normalize the parameters. I'm not sure how sin or cos behave for very large inputs.
As titled, I have an item with a specific position in object space defined by a single vector.
I would like to retrieve the coordinates in camera space of the projection of this vector on the near clipping plane.
In other words, I need the intersection in camera space between this vector and the plane defined by the z coordinate equals to -1 (my near plane).
I needed it for moving object linearly with the mouse in perspective projection
Edit: Right now I go from the object space down to the window space, then from there up to the camera space by setting the window depth window.z equal to 0, that is the near plane.
Note that to get the camera space from the unProject I just pass in as modelview matrix an identity matrix new Mat4(1f):
public Vec3 getCameraSpacePositionOnNearPlane(Vec2i mousePoint) {
int[] viewport = new int[]{0, 0, glViewer.getGlWindow().getWidth(), glViewer.getGlWindow().getHeight()};
Vec3 window = new Vec3();
window.x = mousePoint.x;
window.y = viewport[3] - mousePoint.y - 1;
window.z = 0;
return Jglm.unProject(window, new Mat4(1f), glViewer.getVehicleCameraToClipMatrix(), new Vec4(viewport));
}
Is there a better way (more efficient) to get it without going down to the window space and come back to the camera one?
The most direct approach I could think of would be to simply transform your object space position (let this be called vector x in the following) into eye space, construct a ray from the origin to that eye-space coordinates and calculate the intersection between that ray and the near plane z_eye=-near.
Another approach would be to fully transfrom into the clip space. Since the near plane is z_clip = - w_clip there, you can just set the z coordinate of the result to -w, and project that back to eye space by using the inverse projection matrix.
In both cases, the result will be meaningless if the point lies behind the camera, or at the camera plane z_eye = 0.
Is it possible to use SceneKit's unprojectPoint to convert a 2D point to 3D without having a depth value?
I only need to find the 3D location in the XZ plane. Y can be always 0 or any value since I'm not using it.
I'm trying to do this for iOS 8 Beta.
I had something similar with JavaScript and Three.js (WebGL) like this:
function getMouse3D(x, y) {
var pos = new THREE.Vector3(0, 0, 0);
var pMouse = new THREE.Vector3(
(x / renderer.domElement.width) * 2 - 1,
-(y / renderer.domElement.height) * 2 + 1,
1
);
//
projector.unprojectVector(pMouse, camera);
var cam = camera.position;
var m = pMouse.y / ( pMouse.y - cam.y );
pos.x = pMouse.x + ( cam.x - pMouse.x ) * m;
pos.z = pMouse.z + ( cam.z - pMouse.z ) * m;
return pos;
};
But I don't know how to translate the part with unprojectVector to SceneKit.
What I want to do is to be able to drag an object around in the XZ plane only. The vertical axis Y will be ignored.
Since the object would need to move along a plane, one solution would be to use hitTest method, but I don't think is very good in terms of performance to do it for every touch/drag event. Also, it wouldn't allow the object to move outside the plane either.
I've tried a solution based on the accepted answer here, but it didn't worked. Using one depth value for unprojectPoint, if dragging the object around in the +/-Z direction the object doesn't stay under the finger too long, but it moves away from it instead.
I need to have the dragged object stay under the finger no matter where is it moved in the XZ plane.
First, are you actually looking for a position in the xz-plane or the xy-plane? By default, the camera looks in the -z direction, so the x- and y-axes of the 3D Scene Kit coordinate system go in the same directions as they do in the 2D view coordinate system. (Well, y is flipped by default in UIKit, but it's still the vertical axis.) The xz-plane is then orthogonal to the plane of the screen.
Second, a depth value is a necessary part of converting from 2D to 3D. I'm not an expert on three.js, but from looking at their library documentation (which apparently can't be linked into), their unprojectVector still takes a Vector3. And that's what you're constructing for pMouse in your code above — a vector whose z- and y-coordinates come from the 2D mouse position, and whose z-coordinate is 1.
SceneKit's unprojectPoint works the same way — it takes a point whose z-coordinate refers to a depth in clip space, and maps that to a point in your scene's world space.
If your world space is oriented such that the only variation you care about is in the x- and y-axes, you may pass any z-value you want to unprojectPoint, and ignore the z-value in the vector you get back. Otherwise, pass -1 to map to the far clipping plane, 1 for the near clipping plane, or 0 for halfway in between — the plane whose z-coordinate (in camera space) is 0. If you're using the unprojected point to position a node in the scene, the best advice is to just try different z-values (between -1 and 1) until you get the behavior you want.
However, it's a good idea to be thinking about what you're using an unprojected vector for — if the next thing you'd be doing with it is testing for intersections with scene geometry, look at hitTest: instead.
I'm trying to move multiple sprites (images) in an elliptical path such that distance (arc distance) remains uniform.
I have tried
Move each sprite angle by angle, however the problem with this is that distance moved while moving unit angle around major axis is different than that while moving unit angle around minor axis - hence different distance moved.
Move sprites with just changing x-axis uniformly, however it again moves more around major axis.
So any ideas how to move sprites uniformly without them catching-up/overlapping each other?
Other info:
it will be called in onMouseMove/onTouchMoved so i guess it shouldn't
be much CPU intensive.
Although its a general algorithm question but
if it helps I'm using cocos2d-x
So this is what i ended up doing (which solved it for me):
I moved it in equation of circle and increased angle by 1 degree. Calculated x and y using sin/cos(angle) * radius. And to make it into an ellipse I multiplied it by a factor.
Factor was yIntercept/xIntercept.
so it looked like this in end
FACTOR = Y_INTERCEPT / X_INTERCEPT;
//calculate previous angle
angle = atan((prev_y/FACTOR)/prev_x);
//increase angle by 1 degree (make sure its not radians in your case)
angle++;
//new x and y
x = cos(newangle) * X_INTERCEPT;
y = sin(newangle) * X_INTERCEPT * FACTOR;
I have written a function named getPointOnEllipse that allows you to move your sprites pixel-by-pixel in an elliptical path. The function determines the coordinates of a particular point in the elliptical path, given the coordinates of the center of the ellipse, the lengths of the semi-major axis and the semi-minor axis, and finally the offset of the point into the elliptical path, all in pixels.
Note: To be honest, unfortunately, the getPointOnEllipse function skips (does not detect) a few of the points in the elliptical path. As a result, the arc distance is not exactly uniform. Sometimes it is one pixel, and sometimes two pixels, but not three or more! In spite of the fault, changes in speed will be really "faint", and IMO, your sprites will move pretty smoothly.
Below is the getPointOnEllipse function, along with another function named getEllipsePerimeter, which is used to determine an ellipse's perimeter through Euler's formula. The code is written in JScript.
function getEllipsePerimeter(rx, ry)
{
with (Math)
{
// You'll need to floor the return value to obtain the ellipse perimeter in pixels.
return PI * sqrt(2 * (rx * rx + ry * ry));
}
}
function getPointOnEllipse(cx, cy, rx, ry, d)
{
with (Math)
{
// Note: theta expresses an angle in radians!
var theta = d * sqrt(2 / (rx * rx + ry * ry));
//var theta = 2 * PI * d / getEllipsePerimeter(rx, ry);
return {x:floor(cx + cos(theta) * rx),
y:floor(cy - sin(theta) * ry)};
}
}
The following figure illustrates the parameters of this function:
cx - the x-coordinate of the center of the ellipse
cy - the y-coordinate of the center of the ellipse
rx - the length of semi-major axis
ry - the length of semi-minor axis
d - the offset of the point into the elliptical path (i.e. the arc length from the vertex to the point)
The unit of all parameters is pixel.
The function returns an object containing the x- and y-coordinate of the point of interest, which is represented by a purple ball in the figure.
d is the most important parameter of the getPointOnEllipse function. You should call this function multiple times. In the first call, set d to 0, and then place the sprite at the point returned, which causes the sprite to be positioned on the vertex. Then wait a short period (e.g. 50 milliseconds), and call the function again, setting d parameter to 1. This time, by placing the sprite at the point returned, it moves 1 pixel forward in the ellipse path. Then repeat doing so (wait a short period, call the function with increased d value, and position the sprite) until the value of d reaches the perimeter of the ellipse. You can also increase d value by more than one, so that the sprite moves more pixels forward in each step, resulting in faster movement.
Moreover, you can modify the getEllipsePerimeter function in the code to use a more precise formula (like Ramanujan's formula) for getting ellipse perimeter. But in that case, be sure to modify the getPointOnEllipse function as well to use the second version of theta variable (which is commented in the code). Note that the first version of theta is just a simplified form of the second version for the sake of optimization.
My goal is to move a shape in the virtual world in such a way so that it ends up where the mouse pointer is on the canvas.
What i have:
-mouse position (x,y) on a Canvas3D object
-Point3d object of where a pick ray starting from the Canvas3D viewport intersects with the first scene object. (point in 3D space of where i want to start the drag)
What i want:
-Some way to translate the Point3d's coordinates so that the initial point of intersection (the Point3d object) is always overlapping the the mouse position on the canvas (same as when i used the pick ray to determine what the user clicked on from the Canvas3D object).
Thanks!
It sounds as if you want to use the plane which is parallel to the background plane and contains the intersection point with the object. You can use this plane even when the mouse moves beyond the actual background as it's just a mathematical concept which stretches to infinity.
I'm not a Java programmer so I can't give you code but I am a mathematician so here's equation you need ;)
Let P denote the original intersection point and call the background plane unit normal n. This normal is also the normal of our plane of interest. Let R denote a point on the ray and l denote it's unit direction vector.
Then the equation of the plane is (x-P).n = 0 for a point x in the plane (the . denotes dot product of two vectors). The equation of a point on the ray is x = R + t*l where t is any real number. The ray therefore intersects the plane when
(t*l + R - P).n = 0
i.e. when
t = (P - R).n / ( l.n )
This gives you a t value to plug back into your ray equation to give the intersection point.