I'd like to discuss a problem with my implementation of a Joint Hierarchy.
But let me digress and describe the engine's entities in some detail, and in the order in which they were implemented.

So far I have two instanceable entities which I am perfectly happy with.
They are a textured cube and a textured sphere.

The textured cube instances are very simple - they are constructed in world space (the vertices are explicit), so no transformation is required in order to position them in world space.

The sphere instances are not much more complex - each instance has position (x,y,z) values , I am using glTranslatef to position them, bounded by glPushMatrix and glPopMatrix:

invoke glPushMatrix
invoke glTranslatef, x,y,z
invoke RenderSphere
invoke glPopMatrix

Finally we have the "joint hierarchy" entity. It's a hierarchy of nodes (a linkedlist) in which each node contains a transform matrix.
These matrices describe the transformation required to position and orient each joint with respect to its "parent". For the case of the first node, the parent matrix is Identity matrix.
The logic looks something like this:

localMat = thisMat * parentMat
invoke glLoadMatrix, addr localMat
invoke RenderSphere
iterate for Children, handing them localMat
iterate for Siblings, handing them parentMat

Basically, the transforms are succeeding.
If I set the translation of each joint node by hand, I can see this is so.
The problem I have is that they are "stuck to the camera", which I find bizarre in light of the fact that I am FORCING the modelview matrix.
Note that I am NOT using the matrix stack for this stuff.

If anyone is willing to help me with this issue, I'd surely love to hear from you, I want to know what the heck is going wrong. I am willing to post everything.

TIA, Homer.
Posted on 2004-12-17 00:15:56 by Homer
Since I'm not drawing any responses, I'm going to post my camera class code so that you can see I am not screwing the matrix stack in there.
Note that it incorporates some frustum culling methods so that objects can be culled against the frustum of the active camera :)



.data
kSpeed REAL4 12.0f ;This controls camera travel speed
.code

;Camera Class by Homer
;based on CCamera by DigiBen (gametutorials.com)

Vec3 struct
X REAL4 ?
Y REAL4 ?
Z REAL4 ?
Vec3 ends

class Camera, ,C++ compatible
void PositionCamera:pPos, pView, pUp ;This changes the position, view, and up vector of the camera, primarily used for initialization
void RotateView:fAngle, fX, fY, fZ ;This rotates the camera's view around the position depending on the values passed in.
void SetViewByMouse ;This moves the camera's view by the mouse movements (First person view)
void RotateAroundPoint:pCenter, fX, fY, fZ ;This rotates the camera around a point (I.E. your character).
void StrafeCamera:fspeed ;This strafes the camera left or right depending on the speed (+/-)
void MoveCamera:fspeed ;This will move the camera forward or backward depending on the speed
void CheckForMovement ;This checks for keyboard movement
void Update ;This updates the camera's view and other data (Should be called each frame)
void Look ;This uses gluLookAt() to tell OpenGL where to look
;===
void CullPoint:pPoint ;These methods are used to perform frustum cull testing
void CullCube:pOrigin, fRadius
void CullSphere:pOrigin, fRadius
;===
des vPosition Vec3
des vView Vec3
des vUpVector Vec3
des vStrafe Vec3
float currentRotX ;This tracks the up/down rotation (around X) used to limit rotation (anti-gimbal)
long pFrustum
endclass

;*** NOTE ***
;For those of you who are not familiar with ATC oop programming, please know
;that the above "class definition" defines the Camera struct as follows:
;Camera struct
; vPosition Vec3 <?>
; vView Vec3 <?>
; vUpVector Vec3 <?>
; vStrafe Vec3 <?>
; currentRotX REAL4 ?
; pFrustum DWORD ?
;Camera ends
;
;The only other thing you should be aware of is that on entry to any
;of these "class method" procedures, ecx contains a pointer to the above struct.
;Armed with that knowledge, any "non oop asmcoder" should be able to make sense of this :)

Camera_Camera proc ;The Cam Constructor is called when a Cam is created
mov [ecx].Camera.pFrustum, new (CFrustum) ;<-- Create a Frustum instance owned by this Camera instance
fld1
fst [ecx].Camera.vUpVector.Y ;Init a standard up vector (Rarely ever changes)
fstp [ecx].Camera.vView.Z ;Init a starting view vector (looking up and out the screen)
fld r4_0_5
fstp [ecx].Camera.vView.Y
mov ebx, dwWindowWidth ;Put the mouse in the middle of the screen so that we
shr ebx,1 ;don't rotate the cam unintentionally when we start up
mov eax, dwWindowHeight
shr eax,1
invoke SetCursorPos,ebx, eax
ret
Camera_Camera endp

Camera_$Camera proc ;The Cam Destructor has little work to do
delete [ecx].Camera.pFrustum
ret
Camera_$Camera endp

;This proc used to setup initial view params
Camera_PositionCamera proc uses esi pPos,pView,pUp
mov esi,pPos
fld [esi].Vec3.X
fstp [ecx].Camera.vPosition.X
fld [esi].Vec3.Y
fstp [ecx].Camera.vPosition.Y
fld [esi].Vec3.Z
fstp [ecx].Camera.vPosition.Z

mov esi,pView
fld [esi].Vec3.X
fstp [ecx].Camera.vView.X
fld [esi].Vec3.Y
fstp [ecx].Camera.vView.Y
fld [esi].Vec3.Z
fstp [ecx].Camera.vView.Z

mov esi,pUp
fld [esi].Vec3.X
fld [esi].Vec3.Y
fld [esi].Vec3.Z
fstp [ecx].Camera.vUpVector.Z
fstp [ecx].Camera.vUpVector.Y
fstp [ecx].Camera.vUpVector.X
ret
Camera_PositionCamera endp

Camera_Update proc
local vLook:Vec3 ;<-- The current view vector (the direction we are looking)
local me
mov me,ecx
fld [ecx].Camera.vView.X ;Calculate our "LOOK" vector
fsub [ecx].Camera.vPosition.X ;This is simply (View - Position)
fstp vLook.X
fld [ecx].Camera.vView.Y
fsub [ecx].Camera.vPosition.Y
fstp vLook.Y
fld [ecx].Camera.vView.Z
fsub [ecx].Camera.vPosition.Z
fstp vLook.Z ;Good.. now get CrossProduct of Look and Up vectors (the Strafe vector)
invoke Vec3Cross, addr vLook, addr [ecx].Camera.vUpVector, addr [ecx].Camera.vStrafe
invoke Vec3Normalize, addr [ecx].Camera.vStrafe ; Normalize the strafe vector
icall me, Camera, SetViewByMouse ; Move the camera's view by the mouse
icall me, Camera, CheckForMovement ; This checks to see if the keyboard was pressed
mov ecx,me
icall [ecx].Camera.pFrustum, CFrustum, CalculateFrustum ;Do this whenever the Camera has moved
ret
Camera_Update endp

Camera_StrafeCamera proc fspeed
fld [ecx].Camera.vStrafe.X ; Add the strafe vector to our position
fmul fspeed
fadd [ecx].Camera.vPosition.X
fstp [ecx].Camera.vPosition.X
; fld [ecx].Camera.vStrafe.Y ;(don't strafe in Y)
; fmul fspeed
; fadd [ecx].Camera.vPosition.Y
; fstp [ecx].Camera.vPosition.Y
fld [ecx].Camera.vStrafe.Z
fmul fspeed
fadd [ecx].Camera.vPosition.Z
fstp [ecx].Camera.vPosition.Z

;// Add the strafe vector to our view vector
fld [ecx].Camera.vStrafe.X ; Add the strafe vector to our view vector
fmul fspeed
fadd [ecx].Camera.vView.X
fstp [ecx].Camera.vView.X
; fld [ecx].Camera.vStrafe.Y ;(don't strafe in Y)
; fmul fspeed
; fadd [ecx].Camera.vView.Y
; fstp [ecx].Camera.vView.Y
fld [ecx].Camera.vStrafe.Z
fmul fspeed
fadd [ecx].Camera.vView.Z
fstp [ecx].Camera.vView.Z
ret
Camera_StrafeCamera endp

;Move the camera according to speed and direction of travel
Camera_MoveCamera proc fspeed
local vLook:Vec3
local vAccel:Vec3
local me
; Get the current LOOK vector (the direction we are looking)
mov me,ecx
fld [ecx].Camera.vView.X
fsub [ecx].Camera.vPosition.X
fstp vLook.X
fld [ecx].Camera.vView.Y
fsub [ecx].Camera.vPosition.Y
fstp vLook.Y
fld [ecx].Camera.vView.Z
fsub [ecx].Camera.vPosition.Z
fstp vLook.Z
invoke Vec3Normalize, addr vLook

mov ecx,me

fld vLook.X ;<--Calculate Acceleration vector
fmul fspeed
fst vAccel.X
fadd [ecx].Camera.vPosition.X ;<-- and add it to our Position vector
fstp [ecx].Camera.vPosition.X

fld vLook.Y
fmul fspeed
fst vAccel.Y
fadd [ecx].Camera.vPosition.Y
fstp [ecx].Camera.vPosition.Y

fld vLook.Z
fmul fspeed
fst vAccel.Z
fadd [ecx].Camera.vPosition.Z
fstp [ecx].Camera.vPosition.Z

fld vAccel.X ;<-- Add our acceleration vector to our view vector
fadd [ecx].Camera.vView.X
fstp [ecx].Camera.vView.X
fld vAccel.X
fadd [ecx].Camera.vView.X
fstp [ecx].Camera.vView.X
fld vAccel.X
fadd [ecx].Camera.vView.X
fstp [ecx].Camera.vView.X
ret
Camera_MoveCamera endp

Camera_RotateView proc fAngle, fx, fy, fz
local vLook:Vec3
local sinTheta
local cosTheta, OneMinusCosTheta
local me
; Get the Look vector (The direction we are facing)
mov me,ecx
fld [ecx].Camera.vView.X
fsub [ecx].Camera.vPosition.X
fstp vLook.X
fld [ecx].Camera.vView.Y
fsub [ecx].Camera.vPosition.Y
fstp vLook.Y
fld [ecx].Camera.vView.Z
fsub [ecx].Camera.vPosition.Z
fstp vLook.Z

; Calculate the sine and cosine of the angle once
fld fAngle
fsincos
fstp cosTheta
fstp sinTheta

; Find the new x position for the new rotated point
;vNewView.x = (cosTheta + (1 - cosTheta) * x * x) * vView.x;
fld1
fsub cosTheta
fst OneMinusCosTheta
fmul fx
fmul fx
fadd cosTheta
fmul vLook.X
;vNewView.x += ((1 - cosTheta) * x * y - z * sinTheta) * vView.y;
fld OneMinusCosTheta
fmul fx
fmul fy
fld fz
fmul sinTheta
fadd
fmul vLook.Y
fadd
;vNewView.x += ((1 - cosTheta) * x * z + y * sinTheta) * vView.z;
fld OneMinusCosTheta
fmul fx
fmul fz
fld fy
fmul sinTheta
fadd
fmul vLook.Z
fadd
fadd [ecx].Camera.vPosition.X
fstp [ecx].Camera.vView.X

;--------
; Find the new y position for the new rotated point
;vNewView.y = ((1 - cosTheta) * x * y + z * sinTheta) * vView.x;
fld OneMinusCosTheta
fmul fx
fmul fy
fld fz
fmul sinTheta
fadd
fmul vLook.X
;vNewView.y += (cosTheta + (1 - cosTheta) * y * y) * vView.y;
fld OneMinusCosTheta
fmul fy
fmul fy
fadd cosTheta
fmul vLook.Y
fadd
;vNewView.y += ((1 - cosTheta) * y * z - x * sinTheta) * vView.z;
fld OneMinusCosTheta
fmul fy
fmul fz
fld fx
fmul sinTheta
fsub
fmul vLook.Z
fadd
fadd [ecx].Camera.vPosition.Y
fstp [ecx].Camera.vView.Y

;--------
; Find the new z position for the new rotated point
;NewView.z = ((1 - cosTheta) * x * z - y * sinTheta) * vView.x;
fld OneMinusCosTheta
fmul fx
fmul fz
fld fy
fmul sinTheta
fsub
fmul vLook.X
;NewView.z += ((1 - cosTheta) * y * z + x * sinTheta) * vView.y;
fld OneMinusCosTheta
fmul fy
fmul fz
fld fx
fmul sinTheta
fadd
fmul vLook.Y
fadd
;NewView.z += (cosTheta + (1 - cosTheta) * z * z) * vView.z;
fld OneMinusCosTheta
fmul fz
fmul fz
fadd cosTheta
fmul vLook.Z
fadd
fadd [ecx].Camera.vPosition.Z
fstp [ecx].Camera.vView.Z
ret
Camera_RotateView endp

Camera_SetViewByMouse proc
local me
local middleX, middleY
local angleY, angleZ
local vLook:Vec3
local vRight:Vec3
local mousePos:POINT ; // This is a window structure that holds an X and Y

mov me,ecx

mov eax, dwWindowWidth
shr eax,1
mov middleX,eax
mov eax, dwWindowHeight
shr eax,1
mov middleY,eax
invoke GetCursorPos,addr mousePos ; Get the mouse's current X,Y position

; If our cursor is still in the middle, we never moved... so don't update the screen
mov eax,middleX
mov ebx,middleY
.if mousePos.x == eax && mousePos.y == ebx
ret
.endif

; Set the mouse position to the middle of our window
invoke SetCursorPos,middleX, middleY
mov ecx,me

; Get the direction the mouse moved in, but bring the number down to a reasonable amount
fild middleX
fisub mousePos.x
fdiv r4_500_0
fstp angleY
fild middleY
fisub mousePos.y
fdiv r4_500_0
fstp angleZ

; Calculate the Look vector
fld [ecx].Camera.vView.X
fsub [ecx].Camera.vPosition.X
fstp vLook.X
fld [ecx].Camera.vView.Y
fsub [ecx].Camera.vPosition.Y
fstp vLook.Y
fld [ecx].Camera.vView.Z
fsub [ecx].Camera.vPosition.Z
fstp vLook.Z

; Here we keep track of the current rotation (for up and down) so that
; we can restrict the camera from doing a full 360 loop.
;currentRotX -= angleZ;
mov ecx,me
fld [ecx].Camera.currentRotX
fsub angleZ
fstp [ecx].Camera.currentRotX

; If the current rotation (in radians) is greater than 1.0, we want to cap it.
fld [ecx].Camera.currentRotX
fcomp r4_1_0
__FJLE @F ;if(currentRotX > 1.0f)
fld1
fstp [ecx].Camera.currentRotX ;currentRotX = 1.0f;
jmp NoProbs

@@:
; Check if the rotation is below -1.0, if so we want to make sure it doesn't continue
fld [ecx].Camera.currentRotX
fcomp r4_m1_0 ;else if(currentRotX < -1.0f)
__FJGE @F
fld1
fchs
fstp [ecx].Camera.currentRotX ;currentRotX = -1.0f;
jmp NoProbs

; Otherwise, we can rotate the view around our position
@@: ;else

;THIS CODE IS REDUNDANT BECAUSE WE ALREADY CALCULATED
;THIS VECTOR IN THE CAMERA_UPDATE METHOD
; To find the axis we need to rotate around for up and down
; movements, we need to get a perpendicular vector from the
; camera's view vector and up vector. This will be the axis.
; invoke Vec3Cross, addr vLook, addr [ecx].Camera.vUpVector,addr vRight
; invoke Vec3Normalize, addr vRight

;Rotate around our perpendicular axis and along the y-axis
mov ecx,me
icall me, Camera, RotateView, angleZ, [ecx].Camera.vStrafe.X, [ecx].Camera.vStrafe.Y, [ecx].Camera.vStrafe.Z

NoProbs:
;Rotate around the y axis no matter what the currentRotX is
icall me, Camera, RotateView, angleY,0, r4_1_0, 0
ret
Camera_SetViewByMouse endp

Camera_CheckForMovement proc
local fspeed, fmspeed

fld kSpeed
fmul fTimeElapsed
fst fspeed
fchs
fstp fmspeed

.if bMoveForwards ; Move our camera forward by a positive SPEED
icall ecx, Camera, MoveCamera, fspeed
.endif

.if bMoveBackwards ; Move our camera forward by a positive SPEED
icall ecx, Camera, MoveCamera, fmspeed
.endif

.if bMoveLeft
icall ecx, Camera, StrafeCamera, fmspeed
.endif

.if bMoveRight
icall ecx, Camera, StrafeCamera, fspeed
.endif
ret
Camera_CheckForMovement endp

;This procedure just calls gluLookAt, and it's messy...
Camera_Look proc
local eyeX:REAL8
local eyeY:REAL8
local eyeZ:REAL8
local camX:REAL8
local camY:REAL8
local camZ:REAL8
local upX:REAL8
local upY:REAL8
local upZ:REAL8

;Convert Camera vectors to glDoubles for call to gluLookAt
fld [ecx].Camera.vUpVector.X
fld [ecx].Camera.vUpVector.Y
fld [ecx].Camera.vUpVector.Z
fstp upZ
fstp upY
fstp upX
fld [ecx].Camera.vView.X
fld [ecx].Camera.vView.Y
fld [ecx].Camera.vView.Z
fld [ecx].Camera.vPosition.X
fld [ecx].Camera.vPosition.Y
fld [ecx].Camera.vPosition.Z
fstp eyeZ
fstp eyeY
fstp eyeX
fstp camZ
fstp camY
fstp camX


; Give openGL our camera position, camera view, and camera up vector
push dword ptr upZ[4]
push dword ptr upZ[0]
push dword ptr upY[4]
push dword ptr upY[0]
push dword ptr upX[4]
push dword ptr upX[0]
push dword ptr camZ[4]
push dword ptr camZ[0]
push dword ptr camY[4]
push dword ptr camY[0]
push dword ptr camX[4]
push dword ptr camX[0]
push dword ptr eyeZ[4]
push dword ptr eyeZ[0]
push dword ptr eyeY[4]
push dword ptr eyeY[0]
push dword ptr eyeX[4]
push dword ptr eyeX[0]
call gluLookAt
ret
Camera_Look endp

;====================================================================
; THE FOLLOWING METHODS RELATE ONLY TO FRUSTUM CULLING
;====================================================================
;This procedure performs frustum culling of a Vec3 coordinate (a 3d point)
;Returns TRUE if the point is not visible and thus should be culled
;Returns FALSE if the point is onscreen (inside the frustum).
Camera_CullPoint proc pPoint:ptr Vec3
mov ebx, pPoint
icall [ecx].Camera.pFrustum, CFrustum, PointInFrustum, [ebx], [ebx+4],[ebx+8]
ret
Camera_CullPoint endp

;This procedure performs frustum culling of a 3D Cube given as 3D origin and fRadius
;Returns TRUE if the Cube is not visible and thus should be culled
;Returns FALSE if the Cube is onscreen (inside the frustum).
Camera_CullCube proc pOrigin, fRadius
mov ebx,pOrigin
icall [ecx].Camera.pFrustum, CFrustum, CubeInFrustum, [ebx], [ebx+4],[ebx+8], fRadius
ret
Camera_CullCube endp

;This procedure performs frustum culling of a 3D Sphere given as 3D origin and fRadius
;Returns TRUE if the Sphere is not visible and thus should be culled
;Returns FALSE if the Sphere is onscreen (inside the frustum).
Camera_CullSphere proc pOrigin, fRadius
mov ebx,pOrigin
icall [ecx].Camera.pFrustum, CFrustum, SphereInFrustum, [ebx], [ebx+4],[ebx+8], fRadius
ret
Camera_CullSphere endp

Posted on 2004-12-19 01:17:44 by Homer