Hi,
I started ripping the basic out of my GL code last night so you could see how my camera & hierarchy works. Here's something to mess about with, I'll add in the get x/y/z pitch/yaw/roll stuff later.
Check the source for keys to use in this demo, and have a good mess abotu and see how the parenting looks & works. I've tried to keep it as close to Blitz3Ds as possible.
Which reminds me, you really have to appreciate Marks entity functions in Blitz3D, I've NEVER come across such a cool system in any sample code out there on the net.
Well done BRL! :P
Sorry for sloppy code, I'm absolutely full of flu and can't even enjoy a holiday beer because of medication, BLEH!!! :)
Oh, and I use 2 space tabs in my IDE...
I started ripping the basic out of my GL code last night so you could see how my camera & hierarchy works. Here's something to mess about with, I'll add in the get x/y/z pitch/yaw/roll stuff later.
Check the source for keys to use in this demo, and have a good mess abotu and see how the parenting looks & works. I've tried to keep it as close to Blitz3Ds as possible.
Which reminds me, you really have to appreciate Marks entity functions in Blitz3D, I've NEVER come across such a cool system in any sample code out there on the net.
Well done BRL! :P
Sorry for sloppy code, I'm absolutely full of flu and can't even enjoy a holiday beer because of medication, BLEH!!! :)
Oh, and I use 2 space tabs in my IDE...
Framework brl.basic Import brl.polledinput 'Import pub.glew Import brl.glgraphics Const LOCALSPACE:Int = 1 Const PARENTSPACE:Int = 2 Const WORLDSPACE:Int = 3 InitGL(1024,768,0,0) Local cam1:tCamera = tCamera.Create() Local cam2:tCamera = tCamera.Create() cam1.name="camera1" cam2.name="camera2" 'split screen viewports cam1.setVP 0,0,512,768 cam2.setVP 512,0,512,768 cam2.setpos 1,3,10 Local cube1:tMesh = New tMesh Local cube2:tMesh = New tMesh Local cube3:tMesh = New tMesh cube2.setPos 2,0,0 cube2.setParent cube1,True cube3.setPos 2,2,0 cube3.setParent cube2,True cube1.setPos 0,0,-5 cube1.turn 0,45,0 cube2.turn 0,-45,0 Local piv:tPivot = New tPivot cam2.setParent piv,True cam1.setPos 0,2,0 cube3.setrot 10,20,30 Print cube3.getpitch() Print cube3.getyaw() Print cube3.getroll() Local coordSys:Int = 1 Local c2scale:Float = 1.0 Local sine:Float=0 While Not KeyHit(KEY_ESCAPE) sine:+2 cube3.setscaling 1+Sin(sine)*.5,1+Sin(sine+90)*.5,1 cube2.setScaling c2scale,c2scale,c2scale cam1.SetRot(-(400-MouseY())*.75,(200-MouseX())*.75,0) If KeyHit(KEY_SPACE) coordSys:+1 If coordSys>3 coordsys = 1 End If If KeyDown(KEY_OPENBRACKET) c2scale:- .01 If KeyDown(KEY_CLOSEBRACKET) c2scale:+ .01 If KeyDown(KEY_T) cube1.move 0,0,-.1, coordSys If KeyDown(KEY_G) cube1.move 0,0,.1, coordSys If KeyDown(KEY_F) cube1.move -.1,0,0, coordSys If KeyDown(KEY_H) cube1.move .1,0,0, coordSys If KeyDown(KEY_Q) piv.turn 0,-1,0 If KeyDown(KEY_E) piv.turn 0,1,0 If KeyDown(KEY_W) cam1.move 0,0,-.1', coordSys If KeyDown(KEY_S) cam1.move 0,0,.1', coordSys If KeyDown(KEY_A) cam1.move -.1,0,0', coordSys If KeyDown(KEY_D) cam1.move .1,0,0', coordSys If KeyHit(KEY_1) cube1.setParent cam1,True 'parent If KeyHit(KEY_2) cube1.setParent 0,True 'unparent If MouseDown(1) cube2.turn 1,0,0,coordSys If MouseDown(2) cube2.turn 0,1,0,coordSys piv.turn 0,.5,0 cam2.pointat cam1 Local sx:Float, sy:Float cam2.ScreenProject cam1,sx,sy RenderWorld() glColor3f 1,1,1 glDisable GL_LIGHTING glViewport 0,0,GraphicsWidth(),GraphicsHeight() GLDrawText "Coordinate System: "+coordSys,0,0 GLDrawText "cube2 world position: "+cube2.getX(3)+" , "+cube2.getY(3)+" , "+cube2.getZ(3),0,20 GLDrawText "cam1 world rotation: "+cam1.getPitch(3)+" , "+cam1.getYaw(3)+" , "+cam1.getRoll(3),0,40 GLDrawText "Cam 1",0,748 GLDrawText "Cam 2",512,748 GLDrawText "test",sx,sy GLDrawText "SPACE changes coord sys: 1=local,2=parent,3=world",0,80 GLDrawText "w,s,a,d,mouse controls Cam1. '1' parents cube1 to Cam1, '2' unparents it",0,100 Flip True Wend Function RenderWorld() Local camera:tCamera Local ent:tEntity For camera = EachIn tCamera.cameraList camera.Use() '-------------- 'Render Entitys '-------------- For ent = EachIn tEntity.entityList 'Only pass 'unparented' and 'undrawn' Entitiys to the RenderEntity() 'Function, child Entitys get rendered recursively within the function If ent.parent = Null RenderEntity(ent) End If Next Next End Function Function RenderEntity(obj:Object) 'Save the Current MODELVIEW_MATRIX glPushMatrix() '-------------------------------------------- 'Multiply the current MODELVIEW_MATRIX by 'this entitys Local space Matrix, then apply 'any scaling. Anything drawn after this will 'be drawn in this Entitys 'local space' '-------------------------------------------- glMultMatrixf tEntity(obj).mat.m '----------------------------- 'Only apply the Scaling Matrix 'if it is non-identity '----------------------------- If tEntity(obj).ScaleMode > 0 Then glMultMatrixf tEntity(obj).smat.m '----------------------------------------------- 'Vertex lighting gets affected if the modelview 'matrix is scaled. 'If the Scaling is Uniform (m[0] = m[5] = m[10]) 'we can use GL_RESCALE_NORMAL, which is faster 'than using GL_NORMALIZE ' 'Child entitys MUST check their parent chain for 'any possible scaling, as it will affect them ' 'NOTE: gEntity.ScaleMode is set during calls to 'tEntity.SetScaling() '----------------------------------------------- Local ThisScaleMode:Int = tEntity(obj).ScaleMode Select ThisScaleMode Case 1 'If all Scaling Axis values are equal 'Use Uniform Normal Scaling for speed ' 'Parent scaling should be checked for glEnable GL_RESCALE_NORMAL Case 2 'Non-Uniform Scaling, use slower 'Normalization method 'Parent scaling need not be checked glEnable GL_NORMALIZE Case 0 'Check if any parents are scaled Local pa:tEntity = tEntity(obj).parent While pa And ThisScaleMode < 2 If pa.smat.m[0] <> 1.0 Or pa.smat.m[5] <> 1.0 Or pa.smat.m[10] <> 1.0 ThisScaleMode = 1 End If If ThisScaleMode = 1 End If pa = pa.parent Wend If ThisScaleMode = 1 glEnable GL_RESCALE_NORMAL Else If ThisScaleMode = 2 glEnable GL_NORMZLIZE End If End Select 'Call the Entitys Draw Method here Select obj Case tMesh(obj) tMesh(obj).DrawEntity() Case tPivot(obj) tPivot(obj).DrawEntity() Case tCamera(obj) tCamera(obj).DrawEntity() End Select '--------------------------- 'Disable any Normalize modes '--------------------------- Select ThisScaleMode Case 1 glDisable GL_RESCALE_NORMAL Case 2 glDisable GL_NORMALIZE End Select '------------------------- 'Process any Child entitys '------------------------- For Local ent:tEntity = EachIn tEntity(obj).childList RenderEntity(ent) Next '---------------------------- 'Restore the MODELVIEW_MATRIX '---------------------------- glPopMatrix() End Function Function InitGl(w,h,d,hz) GLGraphics w,h,d,hz 'glewInit() glEnable GL_DEPTH_TEST ' Enable depth testing (Z sorting of pixels) glDepthFunc GL_LESS ' Only draw pixels if the fragment/pixel Z depth, is LESS than the Zbuffer value glFrontFace GL_CW ' Draw polys facing viewer Clockwise 'glEnable GL_CULL_FACE glShadeModel GL_SMOOTH glHint GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST Local position:Float[] = [10.0,10.0,5.0] glEnable GL_LIGHT0 glLightfv GL_LIGHT0, GL_POSITION, position End Function Type tEntity Global entitylist:TList = CreateList() Field parent:tEntity 'this entitys parent Field parentLink:TLink Field name:String Field childList:TList = CreateList() 'list of child entitys (entitys whos parent is THIS entity) Field mat:mat4 = New mat4 'stores *LOCAL* orientation & position Field smat:mat4 = New mat4 'scaling matrix 'Used to tell GL if various scale modes need to be enabled 'If scaling is used, vertex normals are scaled also, this can 'affect the lighting of geometry. GL caters for this by having '2 special auto-normalizing modes. 'if the scaling is uniform (x scale = y scale = z scale) glEnable(GL_RESCALE_NORMAL) 'can be used, it's faster than glEnable(GL_NORMALIZE), which should be used Field scaleMode:Int = 0 Method GetX:Float(world:Int = 1) If world = PARENTSPACE Or parent = Null Then world = LOCALSPACE Select world Case LOCALSPACE Return mat.m[12] Case WORLDSPACE Local v:Float Local pa:tEntity = parent Local tmp:mat4 = New mat4 tmp.Copy(mat) While pa tmp.Multiply(pa.mat) pa = pa.parent Wend Return tmp.m[12] End Select End Method Method GetY:Float(world:Int = 1) If world = PARENTSPACE Or parent = Null Then world = LOCALSPACE Select world Case LOCALSPACE Return mat.m[13] Case WORLDSPACE Local v:Float Local pa:tEntity = parent Local tmp:mat4 = New mat4 tmp.Copy(mat) While pa tmp.Multiply(pa.mat) pa = pa.parent Wend Return tmp.m[13] End Select End Method Method GetZ:Float(world:Int = 1) If world = PARENTSPACE Or parent = Null Then world = LOCALSPACE Select world Case LOCALSPACE Return mat.m[14] Case WORLDSPACE Local v:Float Local pa:tEntity = parent Local tmp:mat4 = New mat4 tmp.Copy(mat) While pa tmp.Multiply(pa.mat) pa = pa.parent Wend Return tmp.m[14] End Select End Method Method GetPitch:Float(world:Byte=1) 'If this entity has no parent then it's world rotation = local rotation If world = LOCALSPACE Or parent = Null Return -ASin(mat.m[9]) Local tmp:Mat4 = New mat4 tmp.Copy(mat) Local par:tEntity If world = PARENTSPACE par = parent tmp.Multiply(par.mat) Return -ASin(tmp.m[9]) Else 'WORLDSPACE par = parent While par tmp.Multiply(par.mat) par = par.parent Wend Return -ASin(tmp.m[9]) End If End Method Method GetYaw:Float(world:Byte=1) 'If this entity has no parent then it's world rotation = local rotation If world = LOCALSPACE Or parent = Null Return ATan2(mat.m[8], mat.m[10]) Local tmp:Mat4 = New mat4 tmp.Copy(mat) Local par:tEntity If world = PARENTSPACE par = parent tmp.Multiply(par.mat) Return ATan2(tmp.m[8], tmp.m[10]) Else 'WORLDSPACE par = parent While par tmp.Multiply(par.mat) par = par.parent Wend Return ATan2(tmp.m[8], tmp.m[10]) End If End Method Method GetRoll:Float(world:Byte=1) 'If this entity has no parent then it's world rotation = local rotation If world = LOCALSPACE Or parent=Null Return ATan2(mat.m[1], mat.m[5]) Local tmp:Mat4 = New mat4 tmp.Copy(mat) Local par:tEntity If world = PARENTSPACE par = parent tmp.Multiply(par.mat) Return ATan2(tmp.m[1], tmp.m[5]) Else 'WORLDSPACE par = parent While par tmp.Multiply(par.mat) par = par.parent Wend Return ATan2(tmp.m[1], tmp.m[5]) End If End Method Method SetPos(px:Float, py:Float, pz:Float, world:Int = 1) If world = LOCALSPACE Or world = PARENTSPACE Or parent = Null mat.m[12] = px mat.m[13] = py mat.m[14] = pz Else Local par:tEntity = parent SetParent(0, 1) mat.m[12] = px mat.m[13] = py mat.m[14] = pz SetParent(par, 1) End If End Method Method Move(px:Float,py:Float,pz:Float,world:Int=1) Local v:Vec3 = Vec3.Create() Local tmat:Mat4 = New mat4 Local pa:tEntity If world = PARENTSPACE If Not parent Then world = WORLDSPACE End If Select world Case LOCALSPACE setGLMatrix() glTranslatef px,py,pz getGLMatrix() Case PARENTSPACE v.Set px,py,pz v.RotVec(mat.m) setGLMatrix() glTranslatef v.x,v.y,v.z getGLMatrix() Case WORLDSPACE v.Set px,py,pz tmat.Copy mat pa = parent While pa tmat.Multiply(pa.mat) pa = pa.parent Wend v.iRotVec(tmat.m) setGLMatrix() glTranslatef v.x,v.y,v.z getGLMatrix() End Select End Method Method Turn( pitchV:Float, yawV:Float, rollV:Float, frame:Int = 1) If frame = PARENTSPACE And parent = Null Then frame = WORLDSPACE Local tmp:Mat4 = New mat4 Select frame 'Rotate about the entitys local axis '----------------------------------- Case LOCALSPACE setGLMatrix() If yawV<>0.0 glRotatef yawV , 0.0, 1.0, 0.0 If pitchV<>0.0 glRotatef pitchV, 1.0, 0.0, 0.0 If rollV<>0.0 glRotatef rollV , 0.0, 0.0, 1.0 'mat.ArbRot(pitchV, yawV, rollV) 'mat.RotateYaw(yawV,0,1,0) 'mat.RotatePitch(pitchV,1,0,0) 'mat.RotateRoll(rollV,0,0,1) getGLMatrix() 'Rotate about the entitys parents axis '------------------------------------- Case PARENTSPACE setGLMatrix() If yawV <> 0.0 glRotatef yawV , mat.m[1], mat.m[5], mat.m[9] If pitchV<> 0.0 glRotatef pitchV, mat.m[0], mat.m[4], mat.m[8] If rollV <> 0.0 glRotatef rollV , mat.m[2], mat.m[6], mat.m[10] getGLMatrix() 'Rotate about the world axis '--------------------------- Case WORLDSPACE Local pa:tEntity tmp.Copy(mat) setGLMatrix() pa = parent 'Multiply the temp matrix up the parent chain While pa tmp.Multiply(pa.mat) pa = pa.parent Wend If yawV <> 0.0 glRotatef yawV , tmp.m[1], tmp.m[5], tmp.m[9] If pitchV<> 0.0 glRotatef pitchV, tmp.m[0], tmp.m[4], tmp.m[8] If rollV <> 0.0 glRotatef rollV , tmp.m[2], tmp.m[6], tmp.m[10] getGLMatrix() End Select End Method Method SetRot(pitchV:Float=0.0, yawV:Float=0.0, rollV:Float=0.0, world:Int=1) Local pa:tEntity mat.m[0]=1.0 ; mat.m[4]=0.0 ; mat.m[8]=0.0 mat.m[1]=0.0 ; mat.m[5]=1.0 ; mat.m[9]=0.0 mat.m[2]=0.0 ; mat.m[6]=0.0 ; mat.m[10]=1.0 If world = LOCALSPACE Turn(pitchV, yawV, rollV) Else If world = PARENTSPACE pa = parent If pa Then mat.Multiply(pa.mat) Turn(pitchV, yawV, rollV,2) Else pa = parent While pa 'mat.postMultiply(pa.mat) mat.Multiply(pa.mat) pa = pa.parent Wend Turn(pitchV, yawV, rollV) End If End Method Method SetScaling(sx:Float=1.0, sy:Float=1.0, sz:Float=1.0) smat.m[0] = sx smat.m[5] = sy smat.m[10] = sz '------------------------------------------------------- 'With regard to what GL normalize mode should be used... 'check if the scaling is uniform in all 3 axis '------------------------------------------------------- If sx = 1.0 And sy = 1.0 And sz = 1.0 ScaleMode = 0 'No Scaling needed Else If sx = sy And sy = sz 'Uniform Scaling ScaleMode = 1 Else ScaleMode = 2 'Non-Uniform Scaling Return End If 'If this entity has no parent, then we don't need to check 'for any parent scaling that would affect us, so exit! If parent = Null Return '---------------------------------------------------- 'If this entity is parented, check all the way up the 'parental chain and check them for uniform scaling '---------------------------------------------------- Local pa:tEntity = parent Local parentScalemode:Int = 0 If pa While pa And parentScalemode <> 2 'Does this entity use scaling? If pa.smat.m[0] <> 1.0 Or pa.smat.m[5] <> 1.0 Or pa.smat.m[10] <> 1.0 parentScalemode = 1 End If 'If it does, is it non uniform? If parentScalemode = 1 'Is it using non-uniform scaling? If pa.smat.m[0] <> pa.smat.m[5] Or pa.smat.m[0] <> pa.smat.m[10] Or pa.smat.m[5] <> pa.smat.m[10] parentScalemode = 2 End If End If 'If the scaling was non uniform then no need to continue If parentScalemode = 2 'force exit! pa = Null Exit End If pa = pa.parent Wend End If Select parentScalemode Case 1 ScaleMode = 1 Case 2 ScaleMode = 2 End Select 'TODO! update scaling mode for an entity when it gets re/un-parented End Method Method SetParent(newParent:tEntity, inplace:Int = False) '-------------------------------------------------------------------- 'If the specified parent entity is the same as the existing one, exit '-------------------------------------------------------------------- If tEntity(newParent) = parent Then Return If Not tEntity(newParent) '----------------------------------------Unparent If inplace = False 'Unparent Localy *WORKING* 'Remove Self from the old Parents list RemoveLink parentLink parent = Null Else 'Unparent Globaly *WORKING* Local pa:tEntity = parent 'Transform matrix into world space While pa mat.Multiply(pa.mat) pa = pa.parent Wend 'Remove Self from the old Parents list RemoveLink parentLink parent = Null End If Else '-------------------------------------------------------- Parent 'Remove Self from any existing parents childlist If parent RemoveLink parentLink parentLink = Null parent = Null End If If inplace = False 'Parent Localy *WORKING* ' Set the parent parent = newParent ' Add Self to the Parents list of Child Entitys parentLink = ListAddLast(newParent.childList, Self) Else 'Parent Globaly *WORKING* 'Remove Self from any existing parents childlist 'and multiply the matrix to leave it in its current 'world space position and rotation If parent Local gpa:tEntity = parent 'Transform matrix into world space While gpa mat.Multiply(gpa.mat) gpa = gpa.parent Wend 'Remove Self from the old Parents list RemoveLink parentLink parentLink = Null parent = Null End If Local parentMat:Mat4 = New mat4 'Build a list of child entitys from world to new parent Local pchain:tEntity = newParent While pchain parentMat.Multiply(pchain.mat) pchain = pchain.parent Wend Local p:Vec3 = New Vec3 p.x = mat.m[12] - parentmat.m[12] p.y = mat.m[13] - parentmat.m[13] p.z = mat.m[14] - parentmat.m[14] 'transforms current matrix rotation into parent space mat.iMatRot(parentMat) 'reposition p.iRotVec(parentMat.m) mat.m[12] = p.x mat.m[13] = p.y mat.m[14] = p.z ' Add Self to the Parents list of Child Entitys parentLink = ListAddLast(newParent.childList,Self) 'Set the new parent parent = newParent End If End If End Method 'Hacky Method PointAt(t:tEntity) Local d:vec3 = vec3.create(t.getX(3), t.getY(3), t.getZ(3)) Local s:vec3 = vec3.create(getX(3), getY(3), getZ(3)) Local s2:vec3 = New vec3 Local bool:Int If s.x<d.x bool = True s2.copy(s) s2.sub(d) s2.normalize() s.y=0 d.y=0 s.sub(d) s.Normalize() mat.LoadIdentity() mat.m[8] = s.x mat.m[9] = s.y mat.m[10] = s.z d.cross2(mat.m[4],mat.m[5],mat.m[6],s.x,s.y,s.z) d.normalize() 'final X mat.m[0] = d.x mat.m[1] = d.y mat.m[2] = d.z Local a:Float = ACos(s2.Dot(s)) If s2.y>s.y a=-a turn a,0,0 'Cater for any parenting Local pa:tEntity = parent While pa mat.iMatrot(pa.mat) pa = pa.parent Wend End Method Method setGLMatrix() glLoadMatrixf mat.m End Method Method getGLMatrix() glGetFloatv GL_MODELVIEW_MATRIX,mat.m End Method End Type Type tPivot Extends tEntity Method New() ListAddLast entityList,Self self.mat = New mat4 self.smat = New mat4 End Method Method DrawEntity() '-------------------- 'Draw some Axis Lines '-------------------- Local al:Float= 0.5 ' Axis Line Length glDisable(GL_LIGHTING) glLineWidth(4.0) glBegin(GL_LINES) glColor3f (1.0,0.0,0.0) glVertex3f(0.0,0.0,0.0) glVertex3f(al,0.0,0.0) glColor3f(0.0,1.0,0.0) glVertex3f(0.0,0.0,0.0) glVertex3f(0.0,al,0.0) glColor3f(0.0,0.0,1.0) glVertex3f(0.0,0.0,0.0) glVertex3f(0.0,0.0,al) glEnd() glEnable(GL_LIGHTING) glLineWidth(1.0) End Method End Type Type tMesh Extends tEntity Method New() ListAddLast entityList,Self self.mat = New mat4 self.smat = New mat4 End Method Method drawentity() glDisable GL_LIGHTING Local sz:Float = 0.5 glBegin GL_QUADS ' // Draw A Quad glColor3f 0.0,1.0,0.0 ' // Set The Color To Green glVertex3f sz, sz,-sz ' // Top Right Of The Quad Top) glVertex3f -sz, sz,-sz ' // Top Left Of The Quad Top) glVertex3f -sz, sz, sz ' // Bottom Left Of The Quad Top) glVertex3f sz, sz, sz ' // Bottom Right Of The Quad Top) glColor3f sz,0.5,0.0 ' // Set The Color To Orange glVertex3f sz,-sz, sz ' // Top Right Of The Quad Bottom) glVertex3f -sz,-sz, sz ' // Top Left Of The Quad Bottom) glVertex3f -sz,-sz,-sz ' // Bottom Left Of The Quad Bottom) glVertex3f sz,-sz,-sz ' // Bottom Right Of The Quad Bottom) glColor3f sz,0.0,0.0 ' // Set The Color To Red glVertex3f sz, sz, sz ' // Top Right Of The Quad Front) glVertex3f -sz, sz, sz ' // Top Left Of The Quad Front) glVertex3f -sz,-sz, sz ' // Bottom Left Of The Quad Front) glVertex3f sz,-sz, sz ' // Bottom Right Of The Quad Front) glColor3f sz,sz,0.0 ' // Set The Color To Yellow glVertex3f sz,-sz,-sz ' // Top Right Of The Quad Back) glVertex3f -sz,-sz,-sz ' // Top Left Of The Quad Back) glVertex3f -sz, sz,-sz ' // Bottom Left Of The Quad Back) glVertex3f sz, sz,-sz ' // Bottom Right Of The Quad Back) glColor3f 0.0,0.0,sz ' // Set The Color To Blue glVertex3f -sz, sz, sz ' // Top Right Of The Quad Left) glVertex3f -sz, sz,-sz ' // Top Left Of The Quad Left) glVertex3f -sz,-sz,-sz ' // Bottom Left Of The Quad Left) glVertex3f -sz,-sz, sz ' // Bottom Right Of The Quad Left) glColor3f sz,0.0,sz ' // Set The Color To Violet glVertex3f sz, sz,-sz ' // Top Right Of The Quad Right) glVertex3f sz, sz, sz ' // Top Left Of The Quad Right) glVertex3f sz,-sz, sz ' // Bottom Left Of The Quad Right) glVertex3f sz,-sz,-sz ' // Bottom Right Of The Quad Right) glEnd ' // Done Drawing The Quad End Method End Type Type tCamera Extends tEntity Global cameraList:TList = CreateList() Global activeCamera:tCamera Field fov:Float 'field of view Field aspect:Float 'usualy screenwidth divided by screenheight Field zNear:Float 'near clip distance Field zFar:Float 'far clip distance Field vPortX:Int Field vPortY:Int Field vPortW:Int Field vPortH:Int Field clearColor:Float[] = [0.5,0.5,0.5,1.0] Field clearDepth:Float = 1.0 Field gridSize:Float = 20.0 Field gridElementSize:Int = 1 Function Create:tCamera() Local c:tCamera = New tCamera c.parent = Null c.vPortX = 0 c.vPortY = 0 c.vPortW = GraphicsWidth() c.vPortH = GraphicsHeight() c.fov = 60 c.aspect = Float(c.vPortW)/Float(c.vPortH) c.zNear = 0.25 c.zFar = 500.0 c.mat = New mat4 c.smat = New mat4 c.scaleMode = 0 ListAddLast entityList,c ListAddLast cameraList,c Return c End Function Method Use() glViewport vPortX,vPortY,vPortW,vPortH glEnable GL_SCISSOR_TEST glScissor vPortX,vPortY,vPortW,vPortH glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT activeCamera = Self 'Setup Projection Matrix ' 'glhPerspective() applies FOV horizontally 'glvPerspective() applies FOV vertically, idnetical to gluPerspective() glMatrixMode GL_PROJECTION glLoadIdentity() glhPerspective() 'Apply camera translation/rotation ' 'GL doesn't really know a Camera exists, what we are actualy doing here 'moving & rotating the WORLD opposite to our cameras settings :) glMatrixMode GL_MODELVIEW glLoadIdentity() 'copy the cameras position/rotation matrix Local tmp:mat4 = New mat4 tmp.Copy(mat) 'Remember we store entitys position/rotation matrix in LOCAL space, so if 'the camera has a parent, we must transform its matrix into WORLD space 'this is done by multiplying it up the parent chain! Local pa:tEntity = parent While pa tmp.Multiply(pa.mat) pa = pa.parent Wend 'Now we apply the matrix to the current OpenGL MODELVIEW matrix 'GL provides a handy function to do this called 'gluLookAt()' 'Here is an alternative that works exactly the same Local view:mat4 = New mat4 Local vRight:Vec3 = Vec3.Create( tmp.m[0], tmp.m[1], tmp.m[2] ) Local vUp:Vec3 = Vec3.Create( tmp.m[4], tmp.m[5], tmp.m[6] ) Local vLook:Vec3 = Vec3.Create( tmp.m[8], tmp.m[9], tmp.m[10] ) Local vEye:Vec3 = Vec3.Create( tmp.m[12], tmp.m[13], tmp.m[14] ) vRight.Normalize() vUp.Normalize() vLook.Normalize() view.m[0] = vRight.x view.m[1] = vUp.x view.m[2] = vLook.x view.m[3] = 0.0 view.m[4] = vRight.y view.m[5] = vUp.y view.m[6] = vLook.y view.m[7] = 0.0 view.m[8] = vRight.z view.m[9] = vUp.z view.m[10] = vLook.z view.m[11] = 0.0 view.m[12] = -vRight.Dot(vEye) view.m[13] = -vUp.Dot(vEye) view.m[14] = -vLook.Dot(vEye) view.m[15] = 1.0 'Finaly apply the transform to the GL_MODELVIEW matrix glMultMatrixf view.m glClearDepth 1.0 glClearColor clearColor[0],clearColor[1],clearColor[2],clearColor[3] glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT glDisable GL_SCISSOR_TEST 'Draw a debug Grid? If gridSize > 1.0 Then DrawGrid() 'Camera is all setup now! End Method Method ScreenProject(t:tEntity, sx:Float Var, sy:Float Var) Local tx:Float, ty:Float, tz:Float Local v:vec3 = New vec3 v.x = t.getX(3) v.y = t.getY(3) v.z = t.getZ(3) Local pm:Float[16] Local mv:Float[16] glGetFloatv GL_PROJECTION_MATRIX,pm glGetFloatv GL_MODELVIEW_MATRIX,mv v.multiply pm v.multiply mv End Method Method SetVP(x:Float, y:Float, w:Float, h:Float) vPortX = x vPortY = y vPortW = w vPortH = h aspect = Float(vPortW)/Float(vPortH) End Method Method DrawGrid() glDisable GL_LIGHTING Local i:Float = -gridSize glColor3f 1,1,1 While i <= gridSize glBegin GL_LINES glVertex3f(i, 0.0, gridSize) glVertex3f(i, 0.0, -gridSize) glEnd glBegin GL_LINES glVertex3f(-gridSize, 0.0, i) glVertex3f(gridSize, 0.0, i) glEnd i:+ gridElementSize Wend End Method Method glhPerspective() Local xmin:Double, xmax:Double, ymin:Double, ymax:Double Local m:Double[16] xmax = zNear * Tan(fov*.5) xmin = -xmax ymin = xmin / aspect ymax = xmax / aspect m[0] = (2.0 * zNear) / (xmax - xmin) m[5] = (2.0 * zNear) / (ymax - ymin) m[10]= -(zFar + zNear) / (zFar - zNear) m[2] = (xmax + xmin) / (xmax - xmin) m[6] = (ymax + ymin) / (ymax - ymin) m[11] = -1 m[14] = -(2.0 * zFar * zNear) / (zFar - zNear) glMultMatrixd m End Method Method DrawEntity() glDisable GL_LIGHTING '-------------------- 'Draw some Axis Lines '-------------------- Local al:Float= 0.2 ' Axis Line Length glDisable(GL_LIGHTING) glLineWidth(2.0) glBegin(GL_LINES) glColor3f (1.0,0.0,0.0) glVertex3f(0.0,0.0,0.0) glVertex3f(al,0.0,0.0) glColor3f(0.0,1.0,0.0) glVertex3f(0.0,0.0,0.0) glVertex3f(0.0,al,0.0) glColor3f(0.0,0.0,1.0) glVertex3f(0.0,0.0,0.0) glVertex3f(0.0,0.0,al) glEnd() glLineWidth(1.0) glColor3f 1.0,1.0,0.0 glBegin(GL_LINES) glVertex3f -.05, .1, .1 glVertex3f .05, .1, .1 glVertex3f .05, .1, .1 glVertex3f .05, .1, .4 glVertex3f .05, .1, .4 glVertex3f -.05, .1, .4 glVertex3f -.05, .1, .4 glVertex3f -.05, .1, .1 glVertex3f -.05, -.1, .1 glVertex3f .05, -.1, .1 glVertex3f .05, -.1, .1 glVertex3f .05, -.1, .4 glVertex3f .05, -.1, .4 glVertex3f -.05, -.1, .4 glVertex3f -.05, -.1, .4 glVertex3f -.05, -.1, .1 glVertex3f -.05, .1, .1 glVertex3f -.05, -.1, .1 glVertex3f .05, .1, .1 glVertex3f .05, -.1, .1 glVertex3f .05, .1, .4 glVertex3f .05, -.1, .4 glVertex3f -.05, .1, .4 glVertex3f -.05, -.1, .4 glVertex3f 0, .1, .4 glVertex3f 0, .15, .45 glVertex3f 0, .15, .45 glVertex3f 0, .25, .45 glVertex3f 0, .25, .45 glVertex3f 0, .3, .4 glVertex3f 0, .3, .4 glVertex3f 0, .3, .3 glVertex3f 0, .3, .3 glVertex3f 0, .25, .25 glVertex3f 0, .25, .25 glVertex3f 0, .3, .2 glVertex3f 0, .3, .2 glVertex3f 0, .3, .1 glVertex3f 0, .3, .1 glVertex3f 0, .25, .05 glVertex3f 0, .25, .05 glVertex3f 0, .15, .05 glVertex3f 0, .15, .05 glVertex3f 0, .1, .1 glVertex3f -.07, .05, 0 glVertex3f .07, .05, 0 glVertex3f .07, .05, 0 glVertex3f .07, -.05, 0 glVertex3f .07, -.05, 0 glVertex3f -.07, -.05, 0 glVertex3f -.07, -.05, 0 glVertex3f -.07, .05, 0 glVertex3f -.07, .05, 0 glVertex3f -.03, .025, .1 glVertex3f .07, .05, 0 glVertex3f .03, .025, .1 glVertex3f .07, -.05, 0 glVertex3f .03, -.025, .1 glVertex3f -.07, -.05, 0 glVertex3f -.03, -.025, .1 glEnd() glEnable GL_LIGHTING glLineWidth 1.0 End Method End Type Type mat4 'OpenGL column major 4x4 Matrix '0 4 8 12 '1 5 9 13 '2 6 10 14 '3 7 11 15 'When used to store orientation & rotation 'm[0],m[1],m[2] = X axis 'm[4],m[5],m[6] = Y axis 'm[8],m[9],m[10] = Z axis 'm[12],m[13],m[14] = Position X,Y,Z 'when used to store Scaling 'm[0] = X scale 'm[5] = Y scale 'm[10] = Z scale Field m:Float[16] Method New() Local i:Int For i = 0 Until 16 m[i] = 0.0 Next 'Make Identity matrix m[0] = 1.0 m[5] = 1.0 m[10] = 1.0 m[15] = 1.0 End Method Method LoadIdentity() For Local i=0 To 11 m[i]=0.0 Next m[0]=1.0 m[5]=1.0 m[10]=1.0 m[15]=1.0 End Method Method Copy(s:mat4) For Local i:Int = 0 To 15 m[i] = s.m[i] Next End Method Method Multiply(mat:Mat4) Local Tempmat:Float[16] Tempmat[0] = m[0] * mat.m[0] + m[1] * mat.m[4] + m[2] * mat.m[8] + m[3] * mat.m[12] Tempmat[1] = m[0] * mat.m[1] + m[1] * mat.m[5] + m[2] * mat.m[9] + m[3] * mat.m[13] Tempmat[2] = m[0] * mat.m[2] + m[1] * mat.m[6] + m[2] * mat.m[10] + m[3] * mat.m[14] Tempmat[3] = m[0] * mat.m[3] + m[1] * mat.m[7] + m[2] * mat.m[11] + m[3] * mat.m[15] Tempmat[4] = m[4] * mat.m[0] + m[5] * mat.m[4] + m[6] * mat.m[8] + m[7] * mat.m[12] Tempmat[5] = m[4] * mat.m[1] + m[5] * mat.m[5] + m[6] * mat.m[9] + m[7] * mat.m[13] Tempmat[6] = m[4] * mat.m[2] + m[5] * mat.m[6] + m[6] * mat.m[10] + m[7] * mat.m[14] Tempmat[7] = m[4] * mat.m[3] + m[5] * mat.m[7] + m[6] * mat.m[11] + m[7] * mat.m[15] Tempmat[8] = m[8] * mat.m[0] + m[9] * mat.m[4] + m[10] * mat.m[8] + m[11] * mat.m[12] Tempmat[9] = m[8] * mat.m[1] + m[9] * mat.m[5] + m[10] * mat.m[9] + m[11] * mat.m[13] Tempmat[10] = m[8] * mat.m[2] + m[9] * mat.m[6] + m[10] * mat.m[10] + m[11] * mat.m[14] Tempmat[11] = m[8] * mat.m[3] + m[9] * mat.m[7] + m[10] * mat.m[11] + m[11] * mat.m[15] Tempmat[12] = m[12] * mat.m[0] + m[13] * mat.m[4] + m[14] * mat.m[8] + m[15] * mat.m[12] Tempmat[13] = m[12] * mat.m[1] + m[13] * mat.m[5] + m[14] * mat.m[9] + m[15] * mat.m[13] Tempmat[14] = m[12] * mat.m[2] + m[13] * mat.m[6] + m[14] * mat.m[10] + m[15] * mat.m[14] Tempmat[15] = m[12] * mat.m[3] + m[13] * mat.m[7] + m[14] * mat.m[11] + m[15] * mat.m[15] For Local i:Int = 0 To 15 m[i]=Tempmat[i] Next End Method Method postMultiply(mat:Mat4) Local Tempmat:Float[16] Tempmat[0] = m[0] * mat.m[0] + m[4] * mat.m[1] + m[8] * mat.m[2] + m[12] * mat.m[3] Tempmat[1] = m[1] * mat.m[0] + m[5] * mat.m[1] + m[9] * mat.m[2] + m[13] * mat.m[3] Tempmat[2] = m[2] * mat.m[0] + m[6] * mat.m[1] + m[10] * mat.m[2] + m[14] * mat.m[3] Tempmat[3] = m[3] * mat.m[0] + m[7] * mat.m[1] + m[11] * mat.m[2] + m[15] * mat.m[3] Tempmat[4] = m[0] * mat.m[4] + m[4] * mat.m[5] + m[8] * mat.m[6] + m[12] * mat.m[7] Tempmat[5] = m[1] * mat.m[4] + m[5] * mat.m[5] + m[9] * mat.m[6] + m[13] * mat.m[7] Tempmat[6] = m[2] * mat.m[4] + m[6] * mat.m[5] + m[10] * mat.m[6] + m[14] * mat.m[7] Tempmat[7] = m[3] * mat.m[4] + m[7] * mat.m[5] + m[11] * mat.m[6] + m[15] * mat.m[7] Tempmat[8] = m[0] * mat.m[8] + m[4] * mat.m[9] + m[8] * mat.m[10] + m[12] * mat.m[11] Tempmat[9] = m[1] * mat.m[8] + m[5] * mat.m[9] + m[9] * mat.m[10] + m[13] * mat.m[11] Tempmat[10] = m[2] * mat.m[8] + m[6] * mat.m[9] + m[10] * mat.m[10] + m[14] * mat.m[11] Tempmat[11] = m[3] * mat.m[8] + m[7] * mat.m[9] + m[11] * mat.m[10] + m[15] * mat.m[11] Tempmat[12] = m[0] * mat.m[12] + m[4] * mat.m[13] + m[8] * mat.m[14] + m[12] Tempmat[13] = m[1] * mat.m[12] + m[5] * mat.m[13] + m[9] * mat.m[14] + m[13] Tempmat[14] = m[2] * mat.m[12] + m[6] * mat.m[13] + m[10] * mat.m[14] + m[14] Tempmat[15] = 1 For Local i:Byte=0 To 15 m[i]=Tempmat[i] Next End Method Method iMatRot(mat:Mat4) Local Tempmat:Float[16] Tempmat[0] = m[0] * mat.m[0] + m[1] * mat.m[1] + m[2] * mat.m[2] Tempmat[1] = m[0] * mat.m[4] + m[1] * mat.m[5] + m[2] * mat.m[6] Tempmat[2] = m[0] * mat.m[8] + m[1] * mat.m[9] + m[2] * mat.m[10] Tempmat[4] = m[4] * mat.m[0] + m[5] * mat.m[1] + m[6] * mat.m[2] Tempmat[5] = m[4] * mat.m[4] + m[5] * mat.m[5] + m[6] * mat.m[6] Tempmat[6] = m[4] * mat.m[8] + m[5] * mat.m[9] + m[6] * mat.m[10] Tempmat[8] = m[8] * mat.m[0] + m[9] * mat.m[1] + m[10] * mat.m[2] Tempmat[9] = m[8] * mat.m[4] + m[9] * mat.m[5] + m[10] * mat.m[6] Tempmat[10] = m[8] * mat.m[8] + m[9] * mat.m[9] + m[10] * mat.m[10] For Local i:Byte=0 To 10 If i<>3 And i<>7 Then m[i] = Tempmat[i] Next End Method Method ArbRot(x:Float, y:Float, z:Float) Local a:Float = Cos(x) Local b:Float = Sin(x) Local c:Float = Cos(y) Local d:Float = Sin(y) Local e:Float = Cos(z) Local f:Float = Sin(z) Local ad:Float = a * d Local bd:Float = b * d m[0] = c * e m[1] = -c * f m[2] = -d m[4] = -bd * e + a * f m[5] = bd * f + a * e m[6] = -b * c m[8] = ad * e + b * f m[9] = -ad * f + b * e m[10] = a * c; 'm[3] = mat[7] = mat[11] = mat[12] = mat[13] = mat[14] = 0; m[15] = 1 End Method End Type Type Vec3 Field x:Float Field y:Float Field z:Float Method New() x=0.0 y=0.0 z=0.0 End Method Method Set(xx:Float, yy:Float, zz:Float) x = xx y = yy z = zz End Method Function Create:Vec3(xx:Float=0.0, yy:Float=0.0, zz:Float=0.0) Local v:Vec3=New Vec3 v.x=xx v.y=yy v.z=zz Return v End Function Method Copy(v:Vec3) x=v.x ; y=v.y ; z=v.z End Method Method Sub(v:Vec3) x:-v.x y:-v.y z:-v.z End Method Method Normalize() Local mag:Float=1.0/Sqr(x*x+y*y+z*z) 'If mag=1.0 Return x:*mag y:*mag z:*mag End Method Method Dot:Float(v:Vec3) 'dot product (angle between 2 vectors) Return x*v.x + y*v.y + z*v.z End Method Method Cross(v1:Vec3, v2:Vec3) x = (v1.y * v2.z) - (v2.y * v1.z) y = (v1.z * v2.x) - (v2.z * v1.x) z = (v1.x * v2.y) - (v2.x * v1.y) End Method Method Cross2(x1#,y1#,z1,x2#,y2#,z2#) x = (y1 * z2) - (y2 * z1) y = (z1 * x2) - (z2 * x1) z = (x1 * y2) - (x2 * y1) End Method Method RotVec(m:Float[]) Local tmp:Float[3] tmp[0] = x*m[0] + y*m[4] + z*m[8] tmp[1] = x*m[1] + y*m[5] + z*m[9] tmp[2] = x*m[2] + y*m[6] + z*m[10] x = tmp[0] y = tmp[1] z = tmp[2] End Method Method iRotVec(m:Float[]) Local tmp:Float[3] tmp[0] = x*m[0] + y*m[1] + z*m[2] tmp[1] = x*m[4] + y*m[5] + z*m[6] tmp[2] = x*m[8] + y*m[9] + z*m[10] x = tmp[0] y = tmp[1] z = tmp[2] End Method 'Transform a Point by a Matrix Method Multiply(m:Float[]) Local tmp:Float[3] tmp[0] = x*m[0] + y*m[4] + z*m[8] + m[12] tmp[1] = x*m[1] + y*m[5] + z*m[9] + m[13] tmp[2] = x*m[2] + y*m[6] + z*m[10]+ m[14] x = tmp[0] y = tmp[1] z = tmp[2] End Method End Type