• Dear Cerberus X User!

    As we prepare to transition the forum ownership from Mike to Phil (TripleHead GmbH), we need your explicit consent to transfer your user data in accordance with our amended Terms and Rules in order to be compliant with data protection laws.

    Important: If you accept the amended Terms and Rules, you agree to the transfer of your user data to the future forum owner!

    Please read the new Terms and Rules below, check the box to agree, and click "Accept" to continue enjoying your Cerberus X Forum experience. The deadline for consent is April 5, 2024.

    Do not accept the amended Terms and Rules if you do not wish your personal data to be transferred to the future forum owner!

    Accepting ensures:

    - Continued access to your account with a short break for the actual transfer.

    - Retention of your data under the same terms.

    Without consent:

    - You don't have further access to your forum user account.

    - Your account and personal data will be deleted after April 5, 2024.

    - Public posts remain, but usernames indicating real identity will be anonymized. If you disagree with a fictitious name you have the option to contact us so we can find a name that is acceptable to you.

    We hope to keep you in our community and see you on the forum soon!

    All the best

    Your Cerberus X Team

MiniB3D bug?

SLotman

Active member
3rd Party Module Dev
Tutorial Author
Joined
Jul 3, 2017
Messages
261
Is the entity parent system on MiniB3D broken?

I'm trying to rotate a dice using the keyboard. This is an old mesh I've used on Blitz3D and in there it works.
What I am doing:

I create a pivot and set it to parent of the 'dice entity'. Then, I rotate that pivot -90 or +90 in X or Y (pitch or yaw), the entity rotates along.
Then I REMOVE the pivot as parent, reset its rotation to 0,0,0 and add it as parent again to the dice entity.

This works flawlessly on Blitz3D. On MiniB3D on Monkey, it fails as the picture below shows: (the text is showing the dice at the top left, the pivot parent and the child 'dice entity'). Sometimes the dice won't update properly?! The weird thing is that it doesn't fail always, just sometimes on specific rotations.

I tought of gimbal lock, but forcing the pivot angles and the dice entity between 0...360 made no difference.
Using "RotateEntity" or "TurnEntity" had the same problem.
I also disabled GC, no difference.

(I'm trying this on GLFW, if it makes any difference... and I can't test it on HTML5, apparently there is something wrong with the code and it fails to compile when using no VBO)

giphy.gif

My dice class:

Cerberus:
Class Dice
    Field mesh:TEntity
    Field pivot:TPivot
    Field state:Int
    Field curAngle:Float, targetAngle:Float
    Field direction:Int
    Field x:Float, y:Float
 
    Method New(x:Int, y:Int, side:Int)
        mesh = CopyEntity(Resources.diceModel)
        pivot = CreatePivot()
        Self.x = x
        Self.y = y
     
        ShowEntity mesh

        Select side
            Case 1
                RotateEntity mesh, 0, -90, 0, True
            Case 2
                RotateEntity mesh, 90, 0, 0, True
            Case 3
                ' do nothing
            Case 4
                RotateEntity mesh, 180, 0, 0, True
            Case 5
                RotateEntity mesh, -90, 0, 0, True
            Case 6
                RotateEntity mesh, 0, 90, 0, True
        End Select    
        PositionEntity mesh, x, y, 0
        PositionEntity pivot, x, y, 0
     
        EntityParent mesh, pivot

        state = 0
    End Method
 
    Method update()
        Local speed:Float = 10
        Select state
            Case 0
                ' do nothing

            Case 1
                ' turning
                Select direction
                    Case Direction.UP                    
                        If curAngle <= targetAngle - speed
                            curAngle+=speed
                            'TurnEntity pivot, speed, 0, 0, True
                            RotateEntity pivot, curAngle, 0, 0
                        Else
                            state = 0
                        End If
                     
                    Case Direction.DOWN
                        If curAngle >= targetAngle + speed
                            curAngle-=speed
                            'TurnEntity pivot, -speed, 0, 0, True
                            RotateEntity pivot, curAngle, 0, 0
                        Else
                            state = 0
                        End If

                    Case Direction.LEFT
                        If curAngle >= targetAngle + speed
                            curAngle-=speed
                            'TurnEntity pivot, 0,-speed, 0, True
                            RotateEntity pivot, 0, curAngle, 0
                        Else
                            state = 0
                        End If

                    Case Direction.RIGHT
                        If curAngle <= targetAngle - speed
                            curAngle+=speed
                            'TurnEntity pivot, 0,speed, 0, True
                            RotateEntity pivot, 0, curAngle, 0
                        Else
                            state = 0
                        End If
                End Select
             
                If state = 0 Then
                    Print "::: END :::"
                End If
        End Select
    End Method
 
    Method turn(dir:Int)
        If state = 0 Then
            Self.direction = dir

            EntityParent mesh, Null        
            RotateEntity pivot, 0, 0,0
            EntityParent mesh, pivot
         
            curAngle = 0

            Select dir
                Case Direction.UP
                    targetAngle = 90
                Case Direction.DOWN
                    targetAngle = 90
                Case Direction.LEFT
                    targetAngle = -90
                Case Direction.RIGHT
                    targetAngle = 90
            End Select
            state = 1
        End If
    End Method
End Class

Am I missing something? Its been ages since I've done anything in 3D, so it might be the case. If anyone can shed a light, please do. I'm stumped here :(
 
Last edited:
Hmmm... same bug happens on MiniB3D on BlitzMax, so its not 'Monkey' or 'Cerberus' at fault...

Edit: It was awful back-porting this to Blitz3D, but in there it works, no glitches. So its a bug on MiniB3D both on BlitzMax AND on Monkey-X / Cerberus :(
 
Last edited:
Just for comparison, Blitzmax code:

Cerberus:
SuperStrict

Import sidesign.minib3d
Graphics3D(800,600,32,2)

THardwareInfo.displayinfo()

AmbientLight 128,128,128

Local light:TLight=CreateLight()
LightColor light, 128,128,128

Global diceModel:TEntity = LoadMesh("dice.b3d")
HideEntity diceModel

Global cam:TCamera
        cam = CreateCamera()
        cam.CameraClsColor(0,0,80)
        cam.PositionEntity 0, 0, -145
        CameraRange cam, 1, 1000

Global dices:Dice[5]

dices[0] = New Dice
dices[0].Create(0,0,1)

While Not KeyHit(KEY_ESCAPE)
    dices[0].update()

    If KeyHit(KEY_LEFT) Then dices[0].turn(7)
    If KeyHit(KEY_RIGHT) Then dices[0].turn(3)
    If KeyHit(KEY_UP) Then dices[0].turn(1)
    If KeyHit(KEY_DOWN) Then dices[0].turn(5)
    
    UpdateWorld

    ' render everything normal now
    RenderWorld
    
    'Text 10,10,"FPS:" + Int(CountFPS())
    Flip 1
    Delay 1
    
Wend

Type Dice
    Field mesh:TEntity
    Field pivot:TPivot
    Field state:Int
    Field curAngle:Float, targetAngle:Float
    Field direction:Int
    Field x:Float, y:Float
    
    Method Create(x:Int, y:Int, side:Int)
        mesh = CopyEntity(diceModel)
        pivot = CreatePivot()
        Self.x = x
        Self.y = y
        
        ShowEntity mesh

        Select side
            Case 1
                RotateEntity mesh, 0, -90, 0, True
            Case 2
                RotateEntity mesh, 90, 0, 0, True
            Case 3
                ' do nothing
            Case 4
                RotateEntity mesh, 180, 0, 0, True
            Case 5
                RotateEntity mesh, -90, 0, 0, True
            Case 6
                RotateEntity mesh, 0, 90, 0, True
        End Select       
        PositionEntity mesh, x, y, 0
        PositionEntity pivot, x, y, 0
        
        EntityParent mesh, pivot

        state = 0
    End Method
    
    Method update()
        Local speed:Float = 10
        Select state
            Case 0
                ' do nothing

            Case 1
                ' turning
                Select direction
                    Case 1                       
                        If curAngle <= targetAngle - speed
                            curAngle = curAngle + speed
                            'TurnEntity pivot, speed, 0, 0, True
                            RotateEntity pivot, curAngle, 0, 0
                        Else
                            state = 0
                        End If
                        
                    Case 5
                        If curAngle >= targetAngle + speed
                            curAngle = curAngle - speed
                            'TurnEntity pivot, -speed, 0, 0, True
                            RotateEntity pivot, curAngle, 0, 0
                        Else
                            state = 0
                        End If

                    Case 7
                        If curAngle >= targetAngle + speed
                            curAngle = curAngle - speed
                            'TurnEntity pivot, 0,-speed, 0, True
                            RotateEntity pivot, 0, curAngle, 0
                        Else
                            state = 0
                        End If

                    Case 3
                        If curAngle <= targetAngle - speed
                            curAngle = curAngle + speed
                            'TurnEntity pivot, 0,speed, 0, True
                            RotateEntity pivot, 0, curAngle, 0
                        Else
                            state = 0
                        End If
                End Select
                
                If state = 0 Then
                    Print "::: END :::"
                End If
        End Select
    End Method
    
    Method turn(dir:Int)
        If state = 0 Then
            Self.direction = dir

            EntityParent mesh, Null           
            RotateEntity pivot, 0, 0,0
            EntityParent mesh, pivot
            
            curAngle = 0

            Select dir
                Case 1
                    targetAngle = 90
                Case 5
                    targetAngle = 90
                Case 7
                    targetAngle = -90
                Case 3
                    targetAngle = 90
            End Select
            state = 1
        End If
    End Method
End Type

Blitz3D code (working):
Cerberus:
Include "..\common\keycodes.bb"

Graphics3D 800,600,32,2

AmbientLight 128,128,128

Local light=CreateLight()
LightColor light, 128,128,128

Global diceModel = LoadMesh("data/dice.b3d")
HideEntity diceModel

Global cam = CreateCamera()
        CameraClsColor(cam, 0,0,80)
        PositionEntity cam, 0, 0, -145
        CameraRange cam, 1, 1000

Dim dices.Dice(5)

dices(0) = New Dice
CreateDice(0,0,0,1)

While Not KeyHit(KEY_ESCAPE)
    updateDice(0)

    If KeyHit(KEY_LEFT) Then turnDice(0, 7)
    If KeyHit(KEY_RIGHT) Then turnDice(0,3)
    If KeyHit(KEY_UP) Then turnDice(0,1)
    If KeyHit(KEY_DOWN) Then turnDice(0,5)
    
    UpdateWorld

    ;' Render everything normal now
    RenderWorld
    
    ;'Text 10,10,"FPS:" + Int(CountFPS())
    Flip 1
    Delay 1
    
Wend

Type Dice
    Field mesh
    Field pivot
    Field state
    Field curAngle#
    Field targetAngle#
    Field direction
    Field x#, y#
End Type

Function CreateDice(index, cx, cy, side)
    dices(index)\mesh = CopyEntity(diceModel)
    dices(index)\pivot = CreatePivot()
    dices(index)\x = cx
    dices(index)\y = cy
        
    ShowEntity dices(index)\mesh

    Select side
        Case 1
            RotateEntity dices(index)\mesh, 0, -90, 0, True
        Case 2
            RotateEntity dices(index)\mesh, 90, 0, 0, True
        Case 3
            ; do nothing
        Case 4
            RotateEntity dices(index)\mesh, 180, 0, 0, True
        Case 5
            RotateEntity dices(index)\mesh, -90, 0, 0, True
        Case 6
            RotateEntity dices(index)\mesh, 0, 90, 0, True
    End Select       
    PositionEntity dices(index)\mesh, dices(index)\x, dices(index)\y, 0
    PositionEntity dices(index)\pivot, dices(index)\x, dices(index)\y, 0
        
    EntityParent dices(index)\mesh, dices(index)\pivot

    dices(index)\state = 0
End Function
    
Function updateDice(index)
    Local speed#= 10
    Select dices(index)\state
        Case 0
            ; do nothing

        Case 1
            ; turning
            Select dices(index)\direction
                Case 1                       
                    If dices(index)\curAngle <= dices(index)\targetAngle - speed
                       dices(index)\curAngle = dices(index)\curAngle + speed
                       ;TurnEntity dices(index)\pivot, dices(index)\speed, 0, 0, True
                       RotateEntity dices(index)\pivot, dices(index)\curAngle, 0, 0
                    Else
                       dices(index)\state = 0
                    End If
                        
                Case 5
                    If dices(index)\curAngle >= dices(index)\targetAngle + speed
                       dices(index)\curAngle = dices(index)\curAngle - speed
                       ;TurnEntity pivot, -speed, 0, 0, True
                       RotateEntity dices(index)\pivot, dices(index)\curAngle, 0, 0
                    Else
                       dices(index)\state = 0
                    End If

                Case 7
                    If dices(index)\curAngle >= dices(index)\targetAngle + speed
                       dices(index)\curAngle = dices(index)\curAngle - speed
                       ;TurnEntity dices(index)\pivot, 0,-dices(index)\speed, 0, True
                       RotateEntity dices(index)\pivot, 0, dices(index)\curAngle, 0
                    Else
                       dices(index)\state = 0
                    End If

                Case 3
                    If dices(index)\curAngle <= dices(index)\targetAngle - speed
                       dices(index)\curAngle = dices(index)\curAngle + speed
                       ;TurnEntity dices(index)\pivot, 0,dices(index)\speed, 0, True
                       RotateEntity dices(index)\pivot, 0, dices(index)\curAngle, 0
                    Else
                       dices(index)\state = 0
                    End If
            End Select
                
            If dices(index)\state = 0 Then
               Print "::: END :::"
            End If
    End Select
End Function
    
Function turnDice(index, dir)
    If dices(index)\state = 0 Then
       dices(index)\direction = dir
       EntityParent dices(index)\mesh, 0           
       RotateEntity dices(index)\pivot, 0, 0,0
       EntityParent dices(index)\mesh, dices(index)\pivot
            
       dices(index)\curAngle = 0

       Select dir
        Case 1
            dices(index)\targetAngle = 90
        Case 5
            dices(index)\targetAngle = -90
        Case 7
            dices(index)\targetAngle = -90
        Case 3
            dices(index)\targetAngle = 90
       End Select
       dices(index)\state = 1
    End If
End Function
 
Even using "OpenB3D"/ "MiniB3D-NG" function "ActTurnTo" the problem persists...
 
Last edited:
Yup, MiniB3D bug. Using OpenMaxB3D on Blitzmax code behaves correctly :(
 
If you know the command that fails, you could compare it to the original source code of Blitz3D on GH.
 
I *don't* know what fails. Even if I did, the original source from B3D is for Direct-X 7, and miniB3D is OpenGL. Completely different beasts :(

I just spent the last 4 hours trying to adapt portions of OpenMaxB3D on MiniB3D, and failed.

I think OpenMaxB3D has a matrix only for rotation, and that's may be why it works there. But adding it on MiniB3D didn't help :(
 
Look at this... this is EntityParent from MiniB3D:
Cerberus:
    Method EntityParent:TEntity(parent_ent:TEntity,glob:Bool=True)

        '' remove old parent

        ' get global values
        Local gpx_:Float=EntityX(True)
        Local gpy_:Float=EntityY(True)
        Local gpz_:Float=EntityZ(True)
        
        Local grx_:Float=EntityPitch(True)
        Local gry_:Float=EntityYaw(True)
        Local grz_:Float=EntityRoll(True)
        
        'Local gsx:Float=EntityScaleX(True) 'gsx is now kept
        'Local gsy:Float=EntityScaleY(True)
        'Local gsz:Float=EntityScaleZ(True)
    
        ' remove self from parent's child list
        If parent<>Null
            parent_link.Remove()
            parent=Null
        Endif

        ' entity no longer has parent, so set local values to equal global values
        ' must get global values before we reset transform matrix with UpdateMat
        px=gpx_
        py=gpy_
        pz=-gpz_
        rx=-grx_
        ry=gry_
        rz=grz_
        'sx=gsx
        'sy=gsy
        'sz=gsz
        

        
        ' No new parent
        If parent_ent=Null
            UpdateMat(True)
            Return
        Endif
        
        ' New parent
    
        If parent_ent<>Null
            
            If glob

                AddParent(parent_ent)
                'UpdateMat()

                PositionEntity(gpx_,gpy_,gpz_,True)
                RotateEntity(grx_,gry_,grz_,True)
                ScaleEntity(gsx,gsy,gsz,True)

            Else
        
                AddParent(parent_ent)
                'UpdateMat()
                
            Endif
            
        Endif
        
        Return self
    End

And THIS is the same thing, from Blitz3D:

C:
void Entity::remove(){
    if( _parent ){
        if( _parent->_children==this ) _parent->_children=_succ;
        if( _parent->_last_child==this ) _parent->_last_child=_pred;
    }else{
        if( _orphans==this ) _orphans=_succ;
        if( _last_orphan==this ) _last_orphan=_pred;
    }
    if( _succ ) _succ->_pred=_pred;
    if( _pred ) _pred->_succ=_succ;
}

void Entity::insert(){
    _succ=0;
    if( _parent ){
        if( _pred=_parent->_last_child ) _pred->_succ=this;
        else _parent->_children=this;
        _parent->_last_child=this;
    }else{
        if( _pred=_last_orphan ) _pred->_succ=this;
        else _orphans=this;
        _last_orphan=this;
    }
}

void Entity::setParent( Entity *p ){
    if( _parent==p ) return;

    remove();

    _parent=p;

    insert();

    invalidateWorld();
}

Not very useful :(
 
It's been so long, but I remember I fixed a lot of bugs that didn't get committed to master yet. I'll see if this was one of them. Ping me again in a week if you haven't heard from me, but I'll try to dust off that old code!
 
Yay! Thanks Adam, can't wait for it :)
 
Great Adam. Thank you, at the moment MiniB3D cannot run in CerberusX.
 
I found the code, but I want to get Cerberus on VSCode (which I have started). Poor Ted is lacking. I'll probably also remove the Xbox/Windows phone and Flash support to help simplify.
 
Last edited:
I haven't gotten to the bug yet, but miniB3D has been updated to work. Testing is still on going.
I'll see if I can get to the bug next.
 
Sorry I've been lagging, technical interview tomorrow, so I've been studying.
 
Good luck on the interview Adam! :)
 
Regarding the rotation problem:

The matrix is updated correctly. The problem is when children are updated, the EntityYaw(true for global) is returning only 0 when EntityPitch is 90 or -90.

I'm debugging now.
 
Using this code, it's easy to see where the problem is. Turns out that the small values supplied to Atan2 are still valid. The runtime environment should be able to handle Atan2 divide by 0 errors, should that occur, because most Atan2 functions use some sort of check.

Cerberus:
Import minib3d

Function Main()
    New Game
End

Class Game Extends App

    Field cam:TCamera
  
    Field light:TLight
  
    Field cube:TMesh,
        dot:TMesh,
        pivot:TPivot,
        cube2:TMesh

    Field txt:TText, txt2:TText
  
    ' used by fps code
    Field old_ms:Int
    Field renders:Int
    Field fps:Int
  
    Field init_gl:Bool = False
  
    Field current_face:Int,
        dir:int,
        dirTick:Int,
        speed:int,
        spinLR:int,
        spinUD:int

    Method OnCreate()
  
        SetUpdateRate 60
        SetRender()
      
    End

    Method Init:int()
      
        If init_gl Then Return 1
  
        If Not TPixmap.PreLoadPixmap(["mojo_font.png"]) Then Return 0
        init_gl = True
      
        cam = CreateCamera()
        cam.CameraClsColor(0,0,80)
        cam.PositionEntity 0.5,1,-5
      
        light=CreateLight(1)
        light.PositionEntity 0,3,-3
      
        dot = CreateSphere(5)
        dot.ScaleEntity(0.2,0.2,0.2)
        dot.EntityColor(0,255,0)

        cube = CreateMiniB3DMonkey()
        cube.ScaleEntity(0.5,0.5,0.5)
      
        pivot = CreatePivot()
        pivot.PositionEntity(-2,0,0)

        cube2 = CreateMiniB3DMonkey()
        cube2.ScaleEntity(0.5,0.5,0.5)
        cube2.EntityParent(pivot, false)

        txt = TText.CreateText2D()
        txt.NoSmooth()
        txt.HideEntity()

        old_ms=Millisecs()
        Print "main: init done"
      
        current_face = 1
        dir = 0
        dirTick = 0
        speed = 10

        Return 1
    End
  
    Method OnUpdate()

        If KeyHit(KEY_CLOSE) Or KeyHit(KEY_ESCAPE) Then Error ""
      
        If Not Init() Then Return

        ' control dice
        Local lr:Int = KeyDown(KEY_RIGHT)-KeyDown(KEY_LEFT)
        Local ud:Int = KeyDown(KEY_DOWN)-KeyDown(KEY_UP)

        if lr = 1 and dirTick =0 then spinLR = 1
        if lr = -1 and dirTick =0 then spinLR = -1
        if ud = 1 and dirTick =0 then spinUD = -1
        if ud = -1 and dirTick =0 then spinUD = 1
      
        if (lr <> 0 or ud <>0 ) then if(dirTick =0) then dirTick = 1

        if(dirTick > 0)     
            if (spinUD <> 0)
                cube.TurnEntity(spinUD * speed,0,0)
                pivot.TurnEntity(spinUD * speed,0,0)
                dirTick += speed
            endif
            if (spinLR <> 0)
                cube.TurnEntity(0, spinLR * speed,0)
                pivot.TurnEntity(0, spinLR * speed,0)
                dirTick += speed
            endif
            if (dirTick>90)
                dirTick =0
                spinUD =0
                spinLR =0
                cube2.EntityParent(null, false)
                pivot.RotateEntity(0,0,0)
                cube2.EntityParent(pivot, true)
            endif
            'Print "p "+cube2.EntityPitch(true)+" y "+cube2.EntityYaw(true)+" r "+cube2.EntityRoll(true)
            'Print "yy "+ (ATan2( cube2.mat.grid[2][0], cube2.mat.grid[2][2]))
        Endif


        txt.SetMode2D() 
        txt.SetText(fps+" fps")
        txt.Draw(0,0)
      
        ' calculate fps
        If Millisecs()-old_ms >= 1000
            old_ms=Millisecs()
            fps=renders
            renders=0
        Endif
      
    End
  
    Method OnRender()
        If Not Init() Then Return
        RenderWorld()
        renders=renders+1
    End

End

Thanks for finding this! I'll update the code and push, but to do a quick fix, remove the a and b checks that set those to 0 for both EntitiyYaw() and EntityPitch().

UPDATE: Github has been updated.
 
Last edited:
Back
Top Bottom