this is just an initial design, but it shows some of the possibilities. this builds off the animator MiniB3D addition i posted earlier. again no modifications to MiniB3D. save this to a file called animator.bmx in the same directory as MiniB3D.bmx.
Import "MiniB3D.bmx"
Import Pub.ColDet
' animator.bmx
Type IEntityAnimator Extends TMesh
Const ORDER_BASE:Int=999999
Method Update()
Local entity:TEntity=GetParent()
If entity Then AnimateEntity(entity,MilliSecs()) Else DebugLog("IEntityAnimator: No Entity")
EndMethod
' called each update to animate an entity
Method AnimateEntity(entity:TEntity,timeMS:Int) Abstract
EndType
Type TColDetCollisionAdapter Extends ICollisionAdapter
Field model:TCollisionModel3D=Null
Method New()
model=New TCollisionModel3D
EndMethod
Function EntityAddAdapter(entity:TEntity,order:Int=0)
Local coll:TColDetCollisionAdapter=TColDetCollisionAdapter.Create(entity)
' set the order so the animator update is called before the update to the entity
EntityOrder(coll,(ORDER_BASE-order))
EndFunction
Function create:TColDetCollisionAdapter(parent_ent:TEntity)
If Not TMesh(parent_ent) Then RuntimeError "TColDetCollisionAdapter.create(): invalid entity passed"
Local coll:TColDetCollisionAdapter=New TColDetCollisionAdapter
coll.class$="TColDetCollisionAdapter"
coll.AddParent(parent_ent)
coll.EntityListAdd(entity_list)
' update matrix
If coll.parent<>Null
coll.mat.Overwrite(coll.parent.mat)
coll.UpdateMat()
Else
coll.UpdateMat(True)
EndIf
' populate the collision model with triangles
coll.FillCollisionModel()
Return coll
EndFunction
' populates the collision model with the mesh triangles
Method FillCollisionModel()
Local entity:TMesh=TMesh(getParent())
' loop through each surface
For Local s:Int=1 To entity.CountSurfaces()
Local surf:TSurface=entity.GetSurface(s)
' loop through each triangle of the surface
For Local t:Int=1 To surf.CountTriangles()
' add each triangle to the collision model
model.addTriangle( ..
surf.VertexX(surf.TriangleVertex(t,0)), ..
surf.VertexY(surf.TriangleVertex(t,0)), ..
surf.VertexZ(surf.TriangleVertex(t,0)), ..
surf.VertexX(surf.TriangleVertex(t,1)), ..
surf.VertexY(surf.TriangleVertex(t,1)), ..
surf.VertexZ(surf.TriangleVertex(t,1)), ..
surf.VertexX(surf.TriangleVertex(t,2)), ..
surf.VertexY(surf.TriangleVertex(t,2)), ..
surf.VertexZ(surf.TriangleVertex(t,2)))
Next
Next
' finalize the model
model.finalize()
EndMethod
Method OnUpdate(entity:TEntity)
' check for a collision
For Local mesh:TEntity=EachIn TEntity.entity_list
If mesh<>Self And mesh.class="TColDetCollisionAdapter"
If model.Collision(TColDetCollisionAdapter(mesh).model)
DebugLog(MilliSecs()+": "+entity.EntityName()+" hit "+mesh.getParent().EntityName())
EndIf
EndIf
Next
EndMethod
' because this is a child, this gets called every time the parent is updated
Method OnUpdateMat(entity:TEntity)
' update the model transformation with the parents matrix
model.setTransform(entity.mat.grid)
EndMethod
'
Method OnFreeEntity(entity:TEntity)
EndMethod
EndType
' collision adapter for entity
' this is meant to store/update collision info for an entity and also to
' allow cleanup via the free entity
' needs to fire after the animators but before the entitys so ORDER_BASE is 10000 lower
Type ICollisionAdapter Extends TMesh
Const ORDER_BASE:Int=989999
Method Update()
Local entity:TEntity=GetParent()
If entity Then OnUpdate(entity) Else DebugLog("ICollisionAdapter: No Entity")
EndMethod
Method UpdateMat(load_identity:Int=False)
Local entity:TEntity=GetParent()
If entity Then OnUpdateMat(entity) Else DebugLog("ICollisionAdapter: No Entity")
EndMethod
Method FreeEntity()
Local entity:TEntity=GetParent()
If entity Then OnFreeEntity(entity) Else DebugLog("ICollisionAdapter: No Entity")
EndMethod
Method OnUpdate(entity:TEntity) Abstract
' this updates when the entity updates
Method OnUpdateMat(entity:TEntity) Abstract
Method OnFreeEntity(entity:TEntity) Abstract
EndType
now for an implementation. again, this is based on the MiniB3D test-alpha.bmx program. save the following code into test-alpha_anim.bmx in the same directory as test-alpha.bmx
Import "../MiniB3D.bmx"
Import BRL.PNGLoader
Import "../animator.bmx"
Local width=640,height=480,depth=16,mode=0
Graphics3D width,height,depth,mode
Local cam=CreateCamera()
Local light=CreateLight()
Local ent1=CreateCube()
Local tex=LoadTexture("media/test.png")
EntityTexture ent1,tex
Local ent2=CopyEntity(ent1)
Local ent3=CopyEntity(ent1)
Local ent4=CopyEntity(ent1)
Local ent5=CopyEntity(ent1)
Local ent6=CopyEntity(ent1)
' -----------------------------
' add a controller to the cam. this could just as easily
' be added to one of the cubes
' -----------------------------
TCameraController.EntityAddAnimator(cam,0)
' -----------------------------
' -----------------------------
' create simple rotation animators and add them to
' ents 1-4
' -----------------------------
TSimpleRotateAnimator.EntityAddAnimator(ent1,.5)
TSimpleRotateAnimator.EntityAddAnimator(ent2,-.5)
TSimpleRotateAnimator.EntityAddAnimator(ent3,1)
TSimpleRotateAnimator.EntityAddAnimator(ent4,-1)
NameEntity(ent1,"ent1")
NameEntity(ent2,"ent2")
NameEntity(ent3,"ent3")
NameEntity(ent4,"ent4")
NameEntity(ent5,"ent5")
NameEntity(ent6,"ent6")
' add some collision detection to all the boxes
TColDetCollisionAdapter.EntityAddAdapter(ent1)
TColDetCollisionAdapter.EntityAddAdapter(ent2)
TColDetCollisionAdapter.EntityAddAdapter(ent3)
TColDetCollisionAdapter.EntityAddAdapter(ent4)
TColDetCollisionAdapter.EntityAddAdapter(ent5)
TColDetCollisionAdapter.EntityAddAdapter(ent6)
' -----------------------------
PositionEntity ent1,0,0,10
PositionEntity ent2,0,0,5
PositionEntity ent3,0,0,20
PositionEntity ent4,0,0,15
PositionEntity ent5,0,0,30
PositionEntity ent6,0,0,25
EntityAlpha ent2,0.5
EntityAlpha ent4,0.5
Local cx#=0
Local cy#=0
Local cz#=0
Local pitch#=0
Local yaw#=0
Local roll#=0
' used by fps code
Local old_ms=MilliSecs()
Local renders
Local fps
While Not KeyDown(KEY_ESCAPE)
If KeyHit(KEY_ENTER) Then DebugStop
RenderWorld
renders=renders+1
' calculate fps
If MilliSecs()-old_ms>=1000
old_ms=MilliSecs()
fps=renders
renders=0
EndIf
GLDrawText "FPS: "+String(fps),0,0
PositionEntity(ent2,0,0,EntityZ(ent2)+.01)
Flip
' problem with freeentity
'FreeEntity(ent6)
Wend
End
' -----------------------------
' begin the animator types
' -----------------------------
' conrols a camera (or potentially any entity) via keyboard
' arrows UP/DOWN/LEFT/RIGHT and keys W/A/S/D
Type TCameraController Extends IEntityAnimator
Field cx#=0
Field cy#=0
Field cz#=0
Field pitch#=0
Field yaw#=0
Field roll#=0
Method AnimateEntity(entity:TEntity,timeMS:Int)
' control camera
If KeyDown(KEY_UP) Then cz#=cz#+1.0
If KeyDown(KEY_LEFT) Then cx#=cx#-1.0
If KeyDown(KEY_RIGHT) Then cx#=cx#+1.0
If KeyDown(KEY_DOWN) Then cz#=cz#-1.0
If KeyDown(KEY_W) Then pitch#=pitch#-1.0
If KeyDown(KEY_A) Then yaw#=yaw#+1.0
If KeyDown(KEY_S) Then pitch#=pitch#+1.0
If KeyDown(KEY_D) Then yaw#=yaw#-1.0
entity.MoveEntity(cx#*0.5,cy#*0.5,cz#*0.5)
entity.RotateEntity(pitch#,yaw#,roll#)
cx#=0
cy#=0
cz#=0
EndMethod
Function EntityAddAnimator(entity:TEntity,order:Int=0)
Local anim:TCameraController=TCameraController.Create(entity)
' set the order so the animator update is called before the update to the entity
EntityOrder(anim,(ORDER_BASE-order))
EndFunction
Function create:TCameraController(parent_ent:TEntity)
Local piv:TCameraController=New TCameraController
piv.class$="TCameraController"
piv.AddParent(parent_ent)
piv.EntityListAdd(entity_list)
' update matrix
If piv.parent<>Null
piv.mat.Overwrite(piv.parent.mat)
piv.UpdateMat()
Else
piv.UpdateMat(True)
EndIf
Return piv
EndFunction
EndType
Type TSimpleRotateAnimator Extends IEntityAnimator
Field rate:Float
Method AnimateEntity(entity:TEntity,timeMS:Int)
entity.TurnEntity(0,rate,0)
EndMethod
Function EntityAddAnimator(entity:TEntity,rate:Float,order:Int=0)
Local anim:TSimpleRotateAnimator=TSimpleRotateAnimator.Create(entity)
anim.rate=rate
' set the order so the animator update is called before the update to the entity
EntityOrder(anim,(ORDER_BASE-order))
EndFunction
Function create:TSimpleRotateAnimator(parent_ent:TEntity)
Local piv:TSimpleRotateAnimator=New TSimpleRotateAnimator
piv.class$="TSimpleRotateAnimator"
piv.AddParent(parent_ent)
piv.EntityListAdd(entity_list)
' update matrix
If piv.parent<>Null
piv.mat.Overwrite(piv.parent.mat)
piv.UpdateMat()
Else
piv.UpdateMat(True)
EndIf
Return piv
EndFunction
EndType
' -----------------------------
' -----------------------------
in the animator.bmx code, there are two new types, one is a interface type meant to be extended. ICollisionAdapter provides a means to store the collision data with the mesh. due to it being an actual child of the mesh, when the parents matrix is updated so is the childs. i use this to create an event. the implementation uses this event to update the model collision transformation.
the second type is the implementation of ICollisionAdapter i called TColDetCollisionAdapter. it acutally implements the creation of the collision data, the updating of the collision matrix, and the checking for a collision.
NOTE: be sure to run this in debug mode. 2 reasons, first, for some reason you will get an unhandle mem error at the first collision. im not sure why but im waiting till the next version of MiniB3D to check it out. ive manually tested outside of MiniB3D and it seems to work fine in debug or runtime. second, you will see the debug output when the collisions occur. you will see the millisecs, followed by the names of the two entities that collided.
this is very basic stuff but it does show a simple collision detection implementation.
enjoy :)
one more time, a big THANK YOU to simonh. cant wait for v0.3!
|