Dark Bit Factory & Gravity
PROGRAMMING => Freebasic => Topic started by: ScottyBrosious on June 25, 2007
-
I am useing opengl.
Is there a way to plot a cylinder from one 3d point to another?
I am getting confused always trying to translatef and rotatef.
Is there an easier way?
-
:goodpost:Hi there,
no there isnt an easier way, you just have to do all the vector maths yourself. I don't have code for this, maybe someone does?
Chris
-
There is this technique i think its called axis angle and it replaces eulers or rotatef`s it points one entity at another sort of like glulookat.
http://dbfinteractive.com/index.php?topic=1874.0
there is even a link to an example in there where i point the camera at the cube but the cube could point to another entity or an invisable point somewhere in space.
-
Take the 2 3d points, subtract the beginning from the end and make it a unit vector. ie. if O is beginning and P is the end, then
Z.x = P.x - O.x
Z.y = P.y - O.y
Z.z = P.z - O.z
;make it a unit
len = sqrt(Z.x * Z.x + Z.y * Z.y + Z.z * Z.z)
Z.x = Z.x / len
Z.y = Z.y / len
Z.z = Z.z / len
That will give you the Z axis of the rotation matrix you need to set.
Then, make another vector which is
Y.x = Z.y
Y.y = -Z.z
Y.z = 0
and make that a unit too.
;make it a unit
len = sqrt(Y.x * Y.x + Y.y * Y.y + Y.z * Y.z)
Y.x = Y.x / len
Y.y = Y.y / len
Y.z = Y.z / len
Then make another vector which is
X.x = Y.y * Z.z - Y.z * Z.y
X.y = Y.z * Z.x - Y.x * Z.z
X.z = Y.x * Z.y - Y.y * Z.x
This is called a cross product.
That gives you a 4x4 OpenGl matrix which looks like this
X.x X.y X.z 0
Y.x Y.y Y.z 0
Z.x Z.y Z.z 0
0 0 0 1
That is then the orientation for the cylinder.
->nino - you might recognise this code - it's exactly the same as the lookat matrix stuff we did a few months ago.
Jim
-
A karma up moment if there ever was one...
-
yep i thought this stuff was black magic or something at first :D its great!
-
Thanks for the explanation.
Could you code me up a small program
showing the code in action?
All's I need is to be able to draw a glucylinder from a 3d point to another.
Thanks
-
Actually, I missed a step...
Once you have X,Y and Z, you need to get the real Y axis
'make it a unit
len = sqr(X.x * X.x + X.y * X.y + X.z * X.z)
X.x = X.x / len
X.y = X.y / len
X.z = X.z / len
Y2.x = X.y * Z.z - X.z * Z.y
Y2.y = X.z * Z.x - X.x * Z.z
Y2.z = X.x * Z.y - X.y * Z.x
That gives you a 4x4 OpenGl matrix which looks like this
X.x X.y X.z 0
Y2.x Y2.y Y2.z 0
Z.x Z.y Z.z 0
0 0 0 1
I realise it's complicated Scotty mate, I'm working on a demo for you...
Jim
-
Here's the code. Attached is the exe and source.
Use the mouse to move the end points around.
Left button+drag - move start point (red)
Right button+drag - move end point (blue)
Hold down shift to move the point in and out of the screen with up/down mouse drag.
'Example of how to align a cylinder between 2 points
'By Jim Shaw
'27/6/2007
'$include: 'GL/gl.bi'
'$include: 'GL/glu.bi'
'$include: 'windows.bi'
option explicit
TYPE vector
x AS SINGLE
y AS SINGLE
z AS SINGLE
END TYPE
declare sub drawblob(byval p as vector, byval colour as uinteger)
declare sub readinput()
dim camang as single
dim screenwidth as uinteger
dim screenheight as uinteger
dim shared init as integer
dim shared ox as integer
dim shared oy as integer
DIM shared O AS vector
DIM shared P as vector
DIM X as vector
dim Y as vector
dim Y2 as vector
dim Z as vector
dim matrix(16) as GLfloat
dim d as single
dim shared sphere as GLUQuadricObj ptr
dim radius as GLdouble
dim length as GLdouble
dim segments as GLint
dim stacks as GLint
dim cylinder as GLUQuadricObj ptr
screenwidth = 640
screenheight = 480
init = 0
SCREEN 18,32,,2
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60,cast(single, screenwidth)/screenheight,1,1024)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)
glClearDepth(1)
'glEnable(GL_TEXTURE_2D)
glClearColor(0,1,0,1)
sphere = gluNewQuadric()
'---------------------------------------------------------------------------------------
' cylinder params
'---------------------------------------------------------------------------------------
cylinder = gluNewQuadric()
gluQuadricNormals(cylinder, GLU_SMOOTH)
gluQuadricTexture(cylinder, GL_TRUE)
radius = 10
segments = 9
stacks = 2
O.x = -100
O.y = -50
O.z = 100
P.x = 90
P.y = 50
P.z = 3
'---------------------------------------------------------------------------------------
' main loop
'---------------------------------------------------------------------------------------
do
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glRotatef(-camang, 0, 0, 1)
glTranslatef(0,0,-512)
drawblob(O,&HFF0000FF)
drawblob(P,&HFFFF0000)
glPushMatrix()
glTranslatef(O.x, O.y, O.z)
Z.x = P.x - O.x
Z.y = P.y - O.y
Z.z = P.z - O.z
'make it a unit
d = sqr(Z.x * Z.x + Z.y * Z.y + Z.z * Z.z)
'store the cylinder's length
length = d
Z.x = Z.x / d
Z.y = Z.y / d
Z.z = Z.z / d
Y.x = Z.y
Y.y = -Z.z
Y.z = 0
'make it a unit
d = sqr(Y.x * Y.x + Y.y * Y.y + Y.z * Y.z)
'check for special case
if d < 0.1 then
Y.x = -Z.z
Y.y = 0
Y.z = Z.x
d = sqr(Y.x * Y.x + Y.y * Y.y + Y.z * Y.z)
end if
Y.x = Y.x / d
Y.y = Y.y / d
Y.z = Y.z / d
'cross product
X.x = Y.y * Z.z - Y.z * Z.y
X.y = Y.z * Z.x - Y.x * Z.z
X.z = Y.x * Z.y - Y.y * Z.x
'make it a unit
d = sqr(X.x * X.x + X.y * X.y + X.z * X.z)
X.x = X.x / d
X.y = X.y / d
X.z = X.z / d
'cross product
Y2.x = X.y * Z.z - X.z * Z.y
Y2.y = X.z * Z.x - X.x * Z.z
Y2.z = X.x * Z.y - X.y * Z.x
matrix( 0)=X.x
matrix( 1)=X.y
matrix( 2)=X.z
matrix( 3)=0
matrix( 4)=Y2.x
matrix( 5)=Y2.y
matrix( 6)=Y2.z
matrix( 7)=0
matrix( 8)=Z.x
matrix( 9)=Z.y
matrix(10)=Z.z
matrix(11)=0
matrix(12)=0
matrix(13)=0
matrix(14)=0
matrix(15)=1
glMultMatrixf(@matrix(0))
glColor3f(1,1,1)
gluCylinder(cylinder, radius, radius, length, segments, stacks)
glPopMatrix()
flip
readinput()
loop until inkey$ = chr$(27)
gluDeleteQuadric(sphere)
gluDeleteQuadric(cylinder)
end
sub drawblob(byval p as vector, byval colour as uinteger)
glColor4ubv(cast(GLubyte ptr,@colour))
glPushMatrix()
glTranslatef(p.x, p.y, p.z)
gluSphere(sphere, 10, 10,10)
glPopMatrix()
end sub
sub readinput()
dim b1 as uinteger
dim b2 as uinteger
dim s as uinteger
dim m as POINT
dim dx as integer
dim dy as integer
b1 = GetAsyncKeyState(VK_LBUTTON)
b2 = GetAsyncKeyState(VK_RBUTTON)
s = GetAsyncKeyState(VK_SHIFT)
GetCursorPos(@m)
if init = 0 then
init = 1
ox = m.x
oy = m.y
end if
dx = m.x - ox
dy = -m.y + oy
ox = m.x
oy = m.y
if b1 <> 0 then
if s<>0 then
O.z = O.z + dy
else
O.x = O.x + dx
O.y = O.y + dy
end if
end if
if b2 <> 0 then
if s<>0 then
P.z = P.z + dy
else
P.x = P.x + dx
P.y = P.y + dy
end if
end if
end sub
I should explain this code a bit. The idea is to create 3 perpendicular (at right angles) vectors, each 1 unit long. Hold out your left hand and point your first finger (Peter Pointer) straight ahead. Point Tommy Thumb straight up, and your middle finder (Tony Tall) to the right. These are the 3 vectors we need, with Peter Pointer pointing along the Z axis of the cylinder.
So, step 1 is to create Peter Pointer, the Z axis, which is just
unit(P-O)
Step 2 is to create a vector which forms a plane with Z. It doesn't matter which one we pick, since no matter how you roll a cylinder around it's Z axis it always looks the same.
Given any vector in 3D, there are 3 vectors which are guaranteed not to lie along it (except in a special case).
These are (0,z,-y) or (-z,0,x) or (y,-z,0). These can easily be worked out by plugging (1,0,0) or (0,1,0) or (0,0,1) into the cross product calculation.
We need then to make this a unit vector, so we have
Y = unit(y,-z,0)
The special case sometimes means that length(Y)=0, so we spot that and pick one of the other two vectors
Y = unit(-z,0,x)
So now we have 2 unit length vectors which form a plane. To find the 3rd vector, Tony Tall, the X axis, we just perform the cross product.
X = cross(Y,Z)
The final step is to form the real Tommy Thumb, the Y axis. Since we have now guaranteed that X is perpendicular to Z, then we can create a new Y from that
Y2 = cross(X,Z)
This guarantees our matrix is going to be square (3 right angles at a corner make a cube, right ;))
Y2 will be a unit vector already, because the cross product between 2 unit vectors which are perpendicular to one another is also a unit vector.
A rotation matrix is just three (unit length) axes in an array. The top row is the X axis, the middle row is the Y axis, and the bottom row is the Z axis. Since OpenGL uses 4x4 matrices, we have to pad with 0s and 1s.
X.x X.y X.z 0
Y2.x Y2.y Y2.z 0
Z.x Z.y Z.z 0
0 0 0 1
and you then just have to glMultMatrix that with the existing modelview to align your object with the axis.
Jim
<edit> updated the attachment to fix the extra line of debugging that got stuck in there, making the cylinders go fat and thin.
-
love it! excellent demo jim k+!
-
Jim,
That sure is some excellent code! I haven't even looked at GL stuff yet - but holy shit you make it so understandable!
Everybody on this forum should thank guys like you for making things easier to understand and share code!
:goodpost:
Drew
PS Karma+