/*
  worldbuilder example.

  Needs: 'bitmap_obj' AND 'explain_exception' from AFC.

  Instructions:   left - right arrow: move observer x
                  up - down    arrow: move observer y
                  Del - Help        : move observer z
                  q - w             : rotate around x
                  a - s             : rotate around y
                  z - x             : rotate around z
                  space             : reset TO defaults
                  Esc               : quit
*/

MODULE 'AFC/worldbuilder',
       'AFC/bitmapper',
       'AFC/explain_exception',
       'intuition/intuition',
       'intuition/screens',
       'graphics/view',
       'exec/ports'


PROC main() HANDLE
  DEF scene:PTR TO worldbuilder
  DEF scr=NIL:PTR TO screen, rp, vp:PTR TO viewport
  DEF win=NIL:PTR TO window, port:PTR TO mp
  DEF msg:PTR TO intuimessage, class, code
  DEF oox,ooy,ooz, rrx,rry,rrz
  DEF bm2:PTR TO bitmapper, rp2, bitmap2, ri:PTR TO rasinfo
  DEF usebm=0, scrbm

  -> fillSinTable() IS done automatically by the following:
  NEW scene.worldbuilder()

  -> we add three objects
  scene.addobj('cube')
  scene.addobj('axis')
  scene.addobj('pyramid')

  -> vertices, lines AND surfaces FOR the 'cube' object_obj:
  ->                 name    x1,y1, z1, x2,y2, z2, ...
  scene.fastsetvert('cube',[-50,50,-50, 50,50,-50, 50,-50,-50, -50,-50,-50,
                -50,50,50, 50,50,50, 50,-50,50, -50,-50,50]:vertex_obj)
  ->                                 MUST be a typed LIST:  ^^^^^^^^^^

  ->                 name   startv1, endv1, colour1, ...
  scene.fastsetline('cube',[0,        1,       2,    1,2,2, 2,3,2, 3,0,2,
                            4,5,2, 5,6,2, 6,7,2, 7,4,2,
                            0,4,2, 1,5,2, 2,6,2, 3,7,2]:line_obj)

  ->                name,num,vertices,colour
  scene.setsurface('cube',0,[0,1,2,3],2)
  scene.setsurface('cube',1,[0,4,5,1],3)
  scene.setsurface('cube',2,[7,6,5,4],4)
  scene.setsurface('cube',3,[3,2,6,7],5)
  scene.setsurface('cube',4,[0,3,7,4],6)
  scene.setsurface('cube',5,[1,5,6,2],7)

  -> definition OF the three axes
  scene.fastsetvert('axis',[0,0,0, 100,0,0, 0,100,0, 0,0,100]:vertex_obj)

  scene.fastsetline('axis',[0,1,3, 0,2,4, 0,3,5]:line_obj)

  -> AND now there comes the 'pyramid' object_obj
  scene.fastsetvert('pyramid',[0,60,-50, 50,60,-50, 50,60,50,
                               0,60,50, 25,120,0]:vertex_obj)

  scene.fastsetline('pyramid',[0,1,6, 1,2,6, 2,3,6, 3,0,6,
                               0,4,6, 1,4,6, 2,4,6, 3,4,6]:line_obj)

  scene.setsurface('pyramid',0,[0,1,2,3],3)
  scene.setsurface('pyramid',1,[0,4,1],2)
  scene.setsurface('pyramid',2,[3,4,0],4)
  scene.setsurface('pyramid',3,[2,4,3],5)
  scene.setsurface('pyramid',4,[1,4,2],6)


  scene.setdisplay(320,256) -> display dimensions
  scene.init3D() -> build system structures FOR filled vectors

  scene.setaftertrasl(160,128) -> origin OF the screen axes.
  scene.setafterscale(200,200) -> scaling AFTER projection (TO fit in
                               -> the screen): 200 = double.
  scene.setobserver(0,0,-200)  -> position OF the observer

  -> we open a screen
  scr:=OpenScreenTagList(NIL,[SA_TOP,0,
                              SA_LEFT,0,
                              SA_WIDTH,320,
                              SA_HEIGHT,256,
                              SA_DEPTH,3,
                              SA_TITLE,'Routines3D',
                              SA_SHOWTITLE,FALSE,
                              0,0])
  IF scr=NIL THEN Raise("SCR")
  vp:=scr.viewport
  ri:=vp.rasinfo    -> we get the rasinfo structure FOR double buffering
  scrbm:=ri.bitmap

  -> a trivial palette
  SetRGB4(vp,3,15,0,0)
  SetRGB4(vp,4,0,15,0)
  SetRGB4(vp,5,0,0,15)
  SetRGB4(vp,7,12,12,0)

  -> we allocate a second bitmap FOR the double buffering
  NEW bm2.bitmapper()
  bm2.allocbitmap(320,256,3,TRUE)
  rp2:=bm2.rastport()
  bitmap2:=bm2.bitmap()

  -> the window IS needed TO get keypresses
  win:=OpenWindowTagList(NIL,[WA_TOP,0,
                              WA_LEFT,0,
                              WA_WIDTH,320,
                              WA_HEIGHT,256,
                              WA_TITLE,'Routines3D',
                              WA_FLAGS,WFLG_ACTIVATE OR WFLG_SMART_REFRESH OR WFLG_BACKDROP OR WFLG_BORDERLESS,
                              WA_IDCMP,IDCMP_RAWKEY,
                              WA_CUSTOMSCREEN,scr,
                              0,0])
  IF win=NIL THEN Raise("WIN")
  rp:=win.rport
  port:=win.userport

  oox:=0 ; ooy:=0 ; ooz:=-200  -> observer initial position
  rrx:=0 ; rry:=0 ; rrz:=0  -> angles OF rotation

  scene.projection()  -> project
  scene.drawfill(rp)  -> AND draw the scene
  usebm:=1-usebm

  LOOP
    Wait(Shl(1,port.sigbit))
    REPEAT
      IF (msg:=GetMsg(port))<>NIL
        class:=msg.class
        code:=msg.code
        ReplyMsg(msg)
        SELECT class
          CASE IDCMP_RAWKEY  -> IF user presses a key...
            SELECT code
              CASE $4C -> up arrow
                ooy:=ooy+1
                scene.setobserver(oox,ooy,ooz)  -> change observer position
              CASE $4D -> down arrow
                ooy:=ooy-1
                scene.setobserver(oox,ooy,ooz)
              CASE $4E -> right arrow
                oox:=oox+1
                scene.setobserver(oox,ooy,ooz)
              CASE $4F -> left arrow
                oox:=oox-1
                scene.setobserver(oox,ooy,ooz)
              CASE $46 -> del
                ooz:=ooz-1
                scene.setobserver(oox,ooy,ooz)
              CASE $5F -> help
                ooz:=ooz+1
                scene.setobserver(oox,ooy,ooz)
              CASE $10 -> q
                rrx:=rrx-1
                scene.setrot(rrx,rry,rrz)  -> SET NEW rotation angles
              CASE $11 -> w
                rrx:=rrx+1
                scene.setrot(rrx,rry,rrz)
              CASE $20 -> a
                rry:=rry-1
                scene.setrot(rrx,rry,rrz)
              CASE $21 -> s
                rry:=rry+1
                scene.setrot(rrx,rry,rrz)
              CASE $31 -> z
                rrz:=rrz-1
                scene.setrot(rrx,rry,rrz)
              CASE $32 -> x
                rrz:=rrz+1
                scene.setrot(rrx,rry,rrz)
              CASE $40 -> space
                oox:=0 ; ooy:=0 ; ooz:=-200
                rrx:=0 ; rry:=0 ; rrz:=0
                scene.setobserver(oox,ooy,ooz)
                scene.setrot(rrx,rry,rrz)
              CASE $45 -> esc
                REPEAT
                  IF (msg:=GetMsg(port))<>NIL THEN ReplyMsg(msg)
                UNTIL msg=NIL
                Raise("end") -> quit
            ENDSELECT
        ENDSELECT
      ENDIF
    UNTIL msg=NIL
    scene.projection()  -> project the scene

    SetAPen(IF usebm=0 THEN rp ELSE rp2, 0)
    RectFill(IF usebm=0 THEN rp ELSE rp2, 0,0,320,256) -> clear the screen

    scene.drawfill(IF usebm=0 THEN rp ELSE rp2) -> eventually draw
    ->    ^^^^^^^^: change this in 'draw' IF you want TO see wireframe objects

    IF usebm=0          -> handmade double buffering (in a screen)
      ri.bitmap:=scrbm
    ELSE                    -> swap the two bitmaps
      ri.bitmap:=bitmap2
    ENDIF
    WaitTOF()
    ScrollVPort(vp)         -> update copperlist TO NEW pointers
    usebm:=1-usebm
  ENDLOOP

EXCEPT DO
  IF win THEN CloseWindow(win)
  IF scr
    ri.bitmap:=scrbm
    CloseScreen(scr)
  ENDIF
  END scene  -> remember TO free ALL memory
  END bm2
  IF exception<>"end" THEN explain_exception()
  CleanUp(0)
ENDPROC

