How do I transform glOrtho clipping planes? - c

I've created an OpenGL application where I define a viewing volume with glOrtho(). Then I use gluLookAt() to transform all the points.
The problem is - as soon as I do this all the points fall out of the clipping plane because it is "left behind". And I end up with a black screen.
How do I change the viewing volume so that it is still tight to my objects once they have been transformed by gluLookAt()?
Here's some code to help better illustrate my problem:
Vector3d eyePos = new Vector3d(0.25, 0.25, 0.6);
Vector3d point = new Vector3d(0, 0, 0.001);
Vector3d up = new Vector3d(0, 1,0);
Matrix4d mat = Matrix4d.LookAt(eyePos, point, up);
GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(0, 1, 0, 1, 0, 1);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.LoadMatrix(ref mat);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);
GL.DepthMask(true);
GL.ClearDepth(1.0);
GL.Color3(Color.Yellow);
GL.Begin(PrimitiveType.Triangles);
GL.Vertex3(0.2, 0.3, 0.01);
GL.Vertex3(0, 0.600, 0.01);
GL.Vertex3(0.600, 0.600, 0.01);
GL.Color3(Color.Blue);
GL.Vertex3(0, 0.6, 0.01);
GL.Vertex3(0.2, 0.3, 0.01);
GL.Vertex3(0.7, 0.5, 0.03);
GL.Color3(Color.Red);
GL.Vertex3(0.100, 0.000, 0.01);
GL.Vertex3(0, 0.0, 0.01);
GL.Vertex3(0.600, 0.600, 0.03);
GL.End();
glControl1.SwapBuffers();
I suppose a more succinct revision of my question is how do I transform two points by the lookat matrix?("mat" in my code)

Okay, I got your problem.
You set the camera position to (0.25, 0.25, 0.6) and with the point to look at your view direction is (-0.25, -0.25, -0.599) (has to be normalised). As I mentioned in my comment below your question, you are drawing a cube in the upper right corner of your view direction.
So the two points (0.25, 0.25, 0.6) (the camera position) and the point defined by the camera position and your view direction, your up vector and your right vector (Have a look at this) define the cube of the things which are shown on the screen (the two points define a diagonal within the cube). Unfortunatly, all of your points are not within this cube.
May it be, that you mixed up the camera position with the point you wanna look at? The change of the two values should solve your problem. The rest of your code seems to be correct. The only thing you have to do is to check whether your points are within the clip space of your projections. Another solution to check where your points are, would be to use, e.g. the keyboard input, to move your camera dynamically.

Related

Rotate SCNNode if user tap on any child node it will point towards camera

Sorry If my question is silly I am new in SceneKit.
I have added a globe using SCNnode sphere and added pins as a child node for that sphere.
Now when user tap on any child node I want to rotate globe to such angle that my child node will come toward center for camera.
Thanks in advance
If I'm understanding correctly, tap on any child and the globe rotates such that it faces you. I "think" you can do it this way.
Provided you have placed your child nodes accurately and they are pointing away from the center, then have lookat constraints on all of your children but set them to empty. Once one is clicked, set the lookat constraint on that child node and point it at the camera node.
func setTarget()
{
node.constraints = []
let vConstraint = SCNLookAtConstraint(target: targetNode)
vConstraint.isGimbalLockEnabled = true
node.constraints = [vConstraint]
}
The more difficult part may be to make sure your child nodes are actually facing away from the center because it will matter. IE: Poke a pencil in a ball and rotate it in your hand. The point of the pencil matters because it's glued to your parent object exactly how you placed it initially. If it was facing the center point of your globe for example, then you'd get the opposite effect.
One way to test it is to create the child nodes with a pointer - in this case, it's just a turret from one of my games.
let BoxGeometry = SCNBox(width: 0.8, height: 0.8, length: 0.8, chamferRadius: 0.0)
let vNode = SCNNode(geometry: BoxGeometry)
BoxGeometry.materials = setDefenseTextures(vGameType: vGameType)
let tubeGeometry = SCNTube(innerRadius: 0.03, outerRadius: 0.05, height: 0.9)
let fireTube = SCNNode(geometry: tubeGeometry)
tubeGeometry.firstMaterial?.diffuse.contents = data.getTextureColor(vTheme: 0, vTextureType: .barrelColor)
fireTube.position = SCNVector3(0, 0.2, -0.3)
let vRotateX = SCNAction.rotateBy(x: CGFloat(Float(GLKMathDegreesToRadians(-90))), y: 0, z: 0, duration: 0)
fireTube.runAction(vRotateX)
vNode.addChildNode(fireTube)
Once you have placed these on your globe and they are rotated correctly, setting lookat constraints "should" work. It should look kind of like a pin cushion (sorry, only example I can think of), and then your globe should rotate towards the camera.
Hopefully

SceneKit: understanding the pivot property of SCNNode

The goal is to increase the length of a SCNBox such that it only grows in the positive-z direction.
This answer suggests playing with the pivot property.
However, the documentation for the pivot property is sparse on the SCNNode page, and there is nothing on the SCNBox page.
Can someone explain how the pivot property works?
Changing a node's pivot is conceptually the same as inserting an intermediate node between this node and its parent. This can be useful in different cases. One example is when the center of the node's geometry isn't where you expect it to be.
For instance if you have an SCNBox, it's bounding box is
min: (-0.5 * width, -0.5 * height, -0.5 * length)
max: (+0.5 * width, +0.5 * height, +0.5 * length)
center: (0.0, 0.0, 0.0)
If you want the length of the SCNBox to only increase in the positive Z axis, then what you want is
min: (-0.5 * width, -0.5 * height, 0.0)
max: (+0.5 * width, +0.5 * height, length)
center: (0.0, 0.0, +0.5 * length)
A geometry's bounding box will never change, but there are ways to arrange nodes and change their bounding boxes.
Solution 1: Intermediate node
One common solution when dealing with transforms is to use intermediate nodes to get a better understanding of how the transforms are applied.
In your case you will want to change the node hierarchy from
- parentNode
| - node
| * geometry
| * transform = SCNMatrix4MakeScale(...)
to
- parentNode
| - intermediateNode
| * transform = SCNMatrix4MakeScale(...)
| | - node
| | * geometry
| | * transform = SCNMatrix4MakeTranslation(0, 0, +0.5 * length)
With this new hierarchy, the center of the bounding box of node is still (0.0, 0.0, 0.0), but the center of the bounding box of intermediateNode is (0.0, 0.0, +0.5 * length).
By scaling intermediateNode instead of node you'll obtain the wanted result.
Solution 2: Pivot
It turns out that's exactly what the pivot property does:
node.pivot = SCNMatrix4MakeTranslation(0, 0, -0.5 * length);
Once you have mentally figured out the transform of the intermediate node, simply set its inverse to the pivot property.
You can find more information about the pivot property here: https://developer.apple.com/reference/scenekit/scnnode/1408044-pivot
This is very similar to Core Animation's anchorPoint property on CALayer, except that in Core Animation the anchor point is specified as relative to the layer's bounding box (goes from 0 to 1 as a percentage of the layer's width and height), while in SceneKit it's absolute.
Say you have a box created like this:
SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
The pivot point will be in the center of that box, you now want to move it to one of the edges. This can be done by translating the pivot node by 0.5. (This is half the width of the box or the distance between the center and the edge.)
boxNode.pivot = SCNMatrix4MakeTranslation(0, 0, -0.5)
The pivot point will now be located at center X, center Y, and zero Z of the object. If you now scale the box it will only grow in the positive Z direction.
Sounds like you want to increase the length of the SCNBox(a geometry). So you can simply increase the length property. The answer you mentioned is about the pivot property. As you can see from the doc:
The pivot point for the node’s position, rotation, and scale.
For example, by setting the pivot to a translation transform you can position a node containing a sphere geometry relative to where the sphere would rest on a floor instead of relative to its center.

How to detect patterns on a 1D array of measurements

I am developing an IA algorithm for a robot that needs to follow a line. The floor will be black, with a white line and there will be different marks that determine different types of "obstacles". I'm using a sensor that gives me an array of 8 measurements of the floor, as seen on the Figure 1 that give me an array of 8 measurements from 0 to 1000, where 0 there is no white and 1000 is totally white. In the examples bellow is a measurement of a white line in the middle of the sensor array and other cases.
int array[] = {50, 24, 9, 960, 1000, 150, 50, 45} // white line in the middle
int array2[] = {50, 24, 9, 960, 1000, 150, 50, 960} // white line in the middle and a square box on the right
int array3[] = {1000, 24, 9, 960, 1000, 150, 50, 40} // white line in the middle and a square box on the left
int array4[] = {1000, 980, 950, 0, 10, 980, 1000, 960} // black square box in the middle
Witch algorithms I could use to detect the patterns on the images below given this array of measurements? I do not want to use several "hardcoded" conditionals as templates, as I think it will not scale well. Im thinking on implementing a "peak counter" algorithm, but I do not know if it will work robust enough.
On the Figures we can see the different cases, the case I want to detect are the ones with the red circle.
Thanks!
How about doing something simple like treating each measurement like an N-dimensional vector. In your case N=8. Then, all you measurements are contained in a hypercube with sides up to length 1000. For N=8 there will be 256 corners. For each of your cases of interest, associate the hypercube corners that best match up to it. Note, some corners may not get associated. Then, for each measurement find its nearest associated hypercube corner. This tells you which case it is. You can mitigate errors by implementing some checks. For example, if the measurement is close to multiple corners (within some uncertainty threshold) then you label the measurement as being ambiguous and skip it.
It's easier to see this for the case of 3 measurements. The 8 corners of the cube could represent
[0,0,0] = no white
[0,0,1] = white on right
[0,1,0] = white in middle
[0,1,1] = white in middle and right
[1,0,0] = white on left
[1,0,1] = white on left and right
[1,1,0] = white on left and middle
[1,1,1] = all white
The case shown below is an ambiguous measurement in the middle.
(source: ctralie.com)

WPF 3D: How to convert a single AxisAngleRotation3D into separate AxisAngleRotation3D?

I have a 3D viewport that uses the TrackBall of WPF 3D tool to rotate the object by mouse. As a result, I get a single AxisAngleRotation3D with Axis vector like (0.5, 0.2, 0.6) and Angle of e.g. 35. That's fine.
What I would like to do is, as an alternative, to give user the ability to rotate the object by individual axis (i.e. x, y, z). So, if the object is rotated around the axis of (0.5, 0.2, 0.6) by 35 degree, how can I convert this into three rotations around each axis, ie. Xangle for vector (1, 0, 0), Yangle for vector (0, 1, 0), and Zangle for vector (0, 0, 1).
I also need a way to convert them back to a single AxisAngleRotation3D object.
EDIT:
I found some stuff at http://www.gamedev.net/reference/articles/article1095.asp. One thing I learnt is that I can easily get a (combined) quaternion from separete AxisAngleRotation3D. For example,
private AxisAngleRotation3D _axisX = new AxisAngleRotation3D();
private AxisAngleRotation3D _axisY = new AxisAngleRotation3D();
private AxisAngleRotation3D _axisZ = new AxisAngleRotation3D();
...
_axisX.Axis = new Vector3D(1, 0, 0);
_axisY.Axis = new Vector3D(0, 1, 0);
_axisZ.Axis = new Vector3D(0, 0, 1);
...
Quaternion qx = new Quaternion(_axisX.Axis, _axisX.Angle);
Quaternion qy = new Quaternion(_axisY.Axis, _axisY.Angle);
Quaternion qz = new Quaternion(_axisZ.Axis, _axisZ.Angle);
Quaternion q = qx * qy * qz;
This is fine. Now, the problem is how can I do the reverse? So, for a given q, how can I find out qx, qy, qz?
You can convert it to a matrix and then back to three separate rotations representation. You can find formulas for both conversions on the net. You can also optimize it by writing it down and solving the equations on paper resulting in possibly faster formula.
However, I suggest to never represent rotation in three numbers, that's due to singularities resulting from this implementation and numeric instability. Use quaternions instead.
cuanternion to Euler angles

WPF: Getting new coordinates after a Rotation

With reference to this programming game I am currently building.
alt text http://img12.imageshack.us/img12/2089/shapetransformationf.jpg
To translate a Canvas in WPF, I am using two Forms: TranslateTransform (to move it), and RotateTransform (to rotate it) [children of the same TransformationGroup]
I can easily get the top left x,y coordinates of a canvas when its not rotated (or rotated at 90deg, since it will be the same), but the problem I am facing is getting the top left (and the other 3 points) coordinates.
This is because when a RotateTransform is applied, the TranslateTransform's X and Y properties are not changed (and thus still indicate that the top-left of the square is like the dotted-square (from the image)
The Canvas is being rotated from its center, so that is its origin.
So how can I get the "new" x and y coordinates of the 4 points after a rotation?
[UPDATE]
alt text http://img25.imageshack.us/img25/8676/shaperotationaltransfor.jpg
I have found a way to find the top-left coordinates after a rotation (as you can see from the new image) by adding the OffsetX and OffsetY from the rotation to the starting X and Y coordinates.
But I'm now having trouble figuring out the rest of the coordinates (the other 3).
With this rotated shape, how can I figure out the x and y coordinates of the remaining 3 corners?
[EDIT]
The points in the 2nd image ARE NOT ACCURATE AND EXACT POINTS. I made the points up with estimates in my head.
[UPDATE] Solution:
First of all, I would like to thank Jason S for that lengthy and Very informative post in which he describes the mathematics behind the whole process; I certainly learned a lot by reading your post and trying out the values.
But I have now found a code snippet (thanks to EugeneZ's mention of TransformBounds) that does exactly what I want:
public Rect GetBounds(FrameworkElement of, FrameworkElement from)
{
// Might throw an exception if of and from are not in the same visual tree
GeneralTransform transform = of.TransformToVisual(from);
return transform.TransformBounds(new Rect(0, 0, of.ActualWidth, of.ActualHeight));
}
Reference: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/86350f19-6457-470e-bde9-66e8970f7059/
If I understand your question right:
given:
shape has corner (x1,y1), center (xc,yc)
rotated shape has corner (x1',y1') after being rotated about center
desired:
how to map any point of the shape (x,y) -> (x',y') by that same rotation
Here's the relevant equations:
(x'-xc) = Kc*(x-xc) - Ks*(y-yc)
(y'-yc) = Ks*(x-xc) + Kc*(y-yc)
where Kc=cos(theta) and Ks=sin(theta) and theta is the angle of counterclockwise rotation. (to verify: if theta=0 this leaves the coordinates unchanged, otherwise if xc=yc=0, it maps (1,0) to (cos(theta),sin(theta)) and (0,1) to (-sin(theta), cos(theta)) . Caveat: this is for coordinate systems where (x,y)=(1,1) is in the upper right quadrant. For yours where it's in the lower right quadrant, theta would be the angle of clockwise rotation rather than counterclockwise rotation.)
If you know the coordinates of your rectangle aligned with the x-y axes, xc would just be the average of the two x-coordinates and yc would just be the average of the two y-coordinates. (in your situation, it's xc=75,yc=85.)
If you know theta, you now have enough information to calculate the new coordinates.
If you don't know theta, you can solve for Kc, Ks. Here's the relevant calculations for your example:
(62-75) = Kc*(50-75) - Ks*(50-85)
(40-85) = Ks*(50-75) + Kc*(50-85)
-13 = -25*Kc + 35*Ks = -25*Kc + 35*Ks
-45 = -25*Ks - 35*Kc = -35*Kc - 25*Ks
which is a system of linear equations that can be solved (exercise for the reader: in MATLAB it's:
[-25 35;-35 -25]\[-13;-45]
to yield, in this case, Kc=1.027, Ks=0.3622 which does NOT make sense (K2 = Kc2 + Ks2 is supposed to equal 1 for a pure rotation; in this case it's K = 1.089) so it's not a pure rotation about the rectangle center, which is what your drawing indicates. Nor does it seem to be a pure rotation about the origin. To check, compare distances from the center of rotation before and after the rotation using the Pythagorean theorem, d2 = deltax2 + deltay2. (for rotation about xc=75,yc=85, distance before is 43.01, distance after is 46.84, the ratio is K=1.089; for rotation about the origin, distance before is 70.71, distance after is 73.78, ratio is 1.043. I could believe ratios of 1.01 or less would arise from coordinate rounding to integers, but this is clearly larger than a roundoff error)
So there's some missing information here. How did you get the numbers (62,40)?
That's the basic gist of the math behind rotations, however.
edit: aha, I didn't realize they were estimates. (pretty close to being realistic, though!)
I use this method:
Point newPoint = rotateTransform.Transform(new Point(oldX, oldY));
where rotateTransform is the instance on which I work and set Angle...etc.
Look at GeneralTransform.TransformBounds() method.
I'm not sure, but is this what you're looking for - rotation of a point in Cartesian coordinate system:
link
You can use Transform.Transform() method on your Point with the same transformations to get a new point to which these transformations were applied.

Resources