'#########################################################
'##
'## "Yet Another Cube Demo"
'##                     (and yet another stupid demo name)
'##
'## By Franck Chevassu (aka. Parabellum)  ---  August 2002
'##
'## Feel free to use any part of this code for your own
'## creations (Some greetings will do! ;)
'##
'#########################################################

S$="  PARABELLUM\n\n... is pleased\nto present his\nlates"
S$=S$+"t demo...\n\n\nThis time it's\nsome 3D stuff!\n\nY"
S$=S$+"ou can change\nthe rendering\nusing the  and\n  ke"
S$=S$+"ys of your\ngamepad.\n\n\nAs always,\ngreetings to"
S$=S$+"\nthe people on\nthe YAB board.\n\nSee you!\nEnjoy"
S$=S$+"..."

open window 640,512 : window origin "cc"

'====== LOGO =============================================
'
' This part initialises the arrays used to draw the logo.

dim letter(9,13)
dim pbl(11,2)

data 3, 1,0, 3,6, 4,3, 2,2
data 3, 1,0, 3,6, 4,0, 2,2
data 4, 1,0, 3,6, 4,4, 2,3, 4,0
data 5, 1,0, 3,6, 4,4, 2,3, 4,0, 1,0
data 3, 4,0, 1,0, 3,6, 4,4
data 1, 2,3, 3,3
data 2, 3,0, 1,0, 3,6
data 3, 4,6, 4,0, 1,0, 2,3
data 4, 1,0, 3,6, 3,4, 4,5, 4,0

data 1,0.9, 2,1,    3,1, 2,1, 4,1, 5,0, 6,1
data 7,0.8, 7,0.75, 8,1, 9,0

for letter=1 to 9
  read lines
  letter(letter,0) = lines
  for lineNb=1 to 2*lines+2 step 2
    read coord : letter(letter,lineNb)   = -14.6*coord
    read coord : letter(letter,lineNb+1) = -14.6*coord
  next
next

for letter=1 to 11
  read pbl(letter,1), spacing
  pbl(letter,2) = spacing * 14.6 * 4.5 * 0.8
next

coord=0 : spacing=0 : lines=0

'====== VARIABLES DECLARATION ============================

iz  = 200
prm = -325
in  = 1        REM -- Set this to "0" to skip the intro...
C   = 0.81     REM -- Color darkness factor (for history)
A   = 10       REM -- Number of history traces + 1

dim S(5760),C(5760)  REM -- Sin/Cos (resolution: 1/16 deg)
for i=0 to 5760
  S(i) = sin(i * PI/2880)
  C(i) = cos(i * PI/2880)
next

mult=120             REM -- Main cube dimension multiplier

                     REM -- main object XYZ rotation speed 
RX =16*0.96  :  RY =16*1.32  :  RZ =16*0.60
                     REM -- sub objects XYZ rotation speed
RX2=16*1.34  :  RY2=16*1.66  :  RZ2=16*2.04

dim col(A,7,3)       REM -- R,G,B for faces in history
dim R(7),G(7),B(7)   REM -- (Optim) RGB for current faces

dim o1(6,4)          REM -- Object 1 faces
dim o2(8,3)          REM -- Object 2 faces
dim o3(12,3)         REM -- Object 3 faces

dim px(8),  py(8),  pz(8)
dim px2(6), py2(6), pz2(6)
dim px3(8), py3(8), pz3(8)
dim tX(8),  tY(8)

HIST=8*A             REM -- History buffer size
dim hX(HIST), hY(HIST), hP(HIST)
dim ntX(8), ntY(8)

dim itX(8),  itY(8),  itZ1(8), itP(6)
dim itX2(6), itY2(6), itZ2(6), itP2(8)
dim itX3(8), itY3(8), itZ3(8), itP3(12)

'====== OBJECT 1 (CUBE) VERTEX DATA ======================

data  1, 1, 1
data  1,-1, 1
data -1,-1, 1
data -1, 1, 1
data  1, 1,-1
data  1,-1,-1
data -1,-1,-1
data -1, 1,-1
for i=1 to 8
  read px(i),py(i),pz(i)
next

'====== OBJECTS 2/3 VERTEX DATA ==========================

data -1, 0,-1
data  1, 0,-1
data  1, 0, 1
data -1, 0, 1
data  0, 1, 0
data  0,-1, 0
for i=1 to 6
  read px2(i),py2(i),pz2(i)
  px3(i)=px2(i)
  py3(i)=py2(i)
  pz3(i)=pz2(i)
next

'====== OBJECT 3 VERTEX DATA =============================

px3(7)=0 : py3(7)=0 : pz3(7)=1
px3(8)=0 : py3(8)=0 : pz3(8)=-1
for i=0 to 5
  px3(i+1) = C(i * 16*60)
  py3(i+1) = S(i * 16*60)
  pz3(i+1) = 0
next

'====== OBJECT 1/3 CONNECTION DATA =======================

data 1,2,3,4
data 2,1,5,6
data 1,4,8,5
data 4,3,7,8
data 3,2,6,7
data 8,7,6,5
for f=1 to 6
  for vx=1 to 4
    read o1(f,vx)    REM -- Fill object 1 faces
  next               REM -- Fill object 3 faces                     
  o3(f,1)=f     :  o3(f,2)=7   :  o3(f,3)=mod(f,6)+1
  o3(f+6,1)=7-f :  o3(f+6,2)=8 :  o3(f+6,3)=mod(11-f,6)+1
next

'====== OBJECT 2 CONNECTION DATA =========================

data 1,2,5
data 2,1,6
data 2,3,5
data 3,2,6
data 1,5,4
data 1,4,6
data 3,4,5
data 4,3,6
for f=1 to 8 : for vx=1 to 3
  read o2(f,vx)    REM -- Fill object 2 faces
next : next

'====== COLOURS SETUP ====================================

data 170,100,100
data 150,150,100
data 100,100,180
data 100,180,100
data  80,130, 90
data 100,150,200
data 150,150,150

for f=1 to 7
  read R(f),G(f),B(f)
  col(1,f,1)=R(f) : col(1,f,2)=G(f) : col(1,f,3)=B(f)
  for i=1 to 3
    col(A,f,i) = C * col(1,f,i)
    for a=A-1 to 2 step -1
      col(a,f,i) = C * col(a+1,f,i)
    next
  next
next

'====== RENDERING SWITCHES ===============================

cel=1          REM -- Cel Shading activated(1) or not(0) ?
mode=1         REM -- Gouraud (1) or Flat (0) shading ?

'====== MAIN LOOP ========================================

LABEL loop

'--- Swap display & work buffers
setdispbuf DB
DB = 1-DB
setdrawbuf DB

'--- Handle gamepad keys
if port<>peek("port1") gosub handleKey

'--- Clear background
gosub background

'--- Compute sub-objects X,Y,Z rotation angles
rX2 = mod( rX2+RX2, 5760 )
rY2 = mod( rY2+RY2, 5760 )
rZ2 = mod( rZ2+RZ2, 5760 )

'--- Setup vars, and compute the transformation matrix
CX=C(rX2) : SX=S(rX2)    REM -- rX2 angle cosinus/sinus
CY=C(rY2) : SY=S(rY2)    REM -- rY2 angle cosinus/sinus
CZ=C(rZ2) : SZ=S(rZ2)    REM -- rZ2 angle cosinus/sinus
gosub matrix

'--- Setup translation variables
dx = SZ * 0.6            REM -- horizontal translation
dy = CY * 0.21           REM -- vertical translation
dz = 1.22 + SX * 0.22    REM -- depth translation

'--- Apply transformation for each sub-objects vertex
for p=1 to 8
' --- Sub object 1 (8 vertices)
  X=px(p) : Y=py(p) : Z=pz(p)
  z       = (m02*X + m12*Y + m22*Z + 1)
  itX(p)  = (m00*X + m10*Y + m20*Z) / z
  itY(p)  = (m01*X + m11*Y + m21*Z) / z
  itZ1(p) = 1 - (z-0.75) * 2.4      REM -- For lightening
' --- Sub object 2 (6 vertices)
  if p<7 then
    X=px2(p): Y=py2(p): Z=pz2(p)
    z       = (m02*X + m12*Y + m22*Z + 1)
    itX2(p) = (m00*X + m10*Y + m20*Z) / z
    itY2(p) = (m01*X + m11*Y + m21*Z) / z
    itZ2(p) = 1 - (z-0.75) * 2.4    REM -- For lightening
  fi
' --- Sub object 3 (8 vertices)
  X=px3(p): Y=py3(p): Z=pz3(p)
  z       = (m02*X + m12*Y + m22*Z + 1)
  itX3(p) = (m00*X + m10*Y + m20*Z) / z
  itY3(p) = (m01*X + m11*Y + m21*Z) / z
  itZ3(p) = 1 - (z-0.75) * 2.4      REM -- For lightening
next

'--- Compute cross product for object 3 faces
for f=1 to 12
  f2 = o3(f,2)
  itP3(f) = (itX3(o3(f,1))-itX3(f2)) * (itY3(o3(f,3))-itY3(f2)) - (itY3(o3(f,1))-itY3(f2)) * (itX3(o3(f,3))-itX3(f2))
next

'--- Compute main cube X,Y,Z rotation angles
rX = mod( rX+RX, 5760 )
rY = mod( rY+RY, 5760 )
rZ = mod( rZ+RZ, 5760 )

'--- Intro stuff (intro is ON as long as in<>0)
if in>0 gosub preIntro

'--- Setup vars, and compute the transformation matrix
CX=C(rX) : SX=S(rX)
CY=C(rY) : SY=S(rY)
CZ=C(rZ) : SZ=S(rZ)
gosub matrix

'--- Advance history index
H = mod( H+8, HIST)

FOR p=1 to 8 rem -----------------------------------------

' --- Compute cross product for object 1 faces (6 faces)
  if p<7 then
    f2 = o1(p,2)
    itP(p) = (itX(o1(p,1))-itX(f2)) * (itY(o1(p,3))-itY(f2)) - (itY(o1(p,1))-itY(f2)) * (itX(o1(p,3))-itX(f2))
  fi

' --- Compute cross product for object 2 faces (8 faces)
  f2 = o2(p,2)
  itP2(p) = (itX2(o2(p,1))-itX2(f2)) * (itY2(o2(p,3))-itY2(f2)) - (itY2(o2(p,1))-itY2(f2)) * (itX2(o2(p,3))-itX2(f2))
  
' --- Apply transformation for main cube vertices
  X=px(p): Y=py(p): Z=pz(p)
  z       = (m02*X + m12*Y + m22*Z + dz) / mult
  tX(p)   = (m00*X + m10*Y + m20*Z + dx) / z + introX
  tY(p)   = (m01*X + m11*Y + m21*Z + dy) / z

' --- Record the transformed vertex coords in history
  hX(H+p) = tX(p)
  hY(H+p) = tY(p)

NEXT rem -------------------------------------------------

'--- Draw history cubes (oldest first -> darkest first)
h = H
for i=2 to A           REM -- There are (A-1) elts (cubes)
  h = mod( h+8, HIST)  REM -- Advance/wrap around history
  for f=1 to 6         REM -- Each cube has 6 faces
    if hP(h+f)<0 then  REM -- Back face culling
      v1=h+o1(f,1) : v2=h+o1(f,2)
      v3=h+o1(f,3) : v4=h+o1(f,4)
      setrgb 1, col(i,f,1), col(i,f,2), col(i,f,3)
      fill triangle hX(v1),hY(v1) to hX(v2),hY(v2) to hX(v3),hY(v3)
      fill triangle hX(v3),hY(v3) to hX(v4),hY(v4) to hX(v1),hY(v1)
    fi
  next
next

'--- For each face of the main cube...
for f=1 to 6
  f1 = o1(f,1): f2 = o1(f,2) : f3 =o1(f,3) : f4 =o1(f,4)
  x1 = tX(f1) : y1 = tY(f1)
  x2 = tX(f2) : y2 = tY(f2)
  x3 = tX(f3) : y3 = tY(f3)
' --- Check cross product to know if face is visible
  if (x1-x2) * (y3-y2) - (x3-x2) * (y1-y2) >= 0 then
    hP(H+f) = 0     REM -- This face is not visible
  else
    hP(H+f) = -1    REM -- This face is visible
'   --- Draw that face
    setrgb 1, R(f),G(f),B(f)
    fill triangle x1,y1 to x2,y2 to x3,y3
    fill triangle x1,y1 to x3,y3 to tX(f4),tY(f4)
'   --- Draw its sub-obj content (if intro mode allows it)
    if in=0 or in>4 then
      if f=1 or f=6 then         REM -- Face 1/6 => obj 2
        d2 = pz(o1(f,1))
        d0=d2*m20+dx : d1=d2*m21+dy : d2=d2*m22+dz
'       --- Project the vertices onto the cube face
        for p=1 to 6
          X = itX2(p) * 0.58823
          Y = itY2(p) * 0.58823
          z      = (m02*X + m12*Y + d2) / mult
          ntX(p) = (m00*X + m10*Y + d0) / z
          ntY(p) = (m01*X + m11*Y + d1) / z
        next
        gosub object2            REM -- Draw sub object 2
      elsif f=2 or f=4 then      REM -- Face 2/4 => obj 3
        d2 = px(o1(f,2))
        d0=d2*m00+dx : d1=d2*m01+dy : d2=d2*m02+dz
'       --- Project the vertices onto the cube face
        for p=1 to 8
          Z = itX3(p) * 0.76923
          Y = itY3(p) * 0.76923
          z      = (d2 + m12*Y + m22*Z) / mult
          ntX(p) = (d0 + m10*Y + m20*Z) / z
          ntY(p) = (d1 + m11*Y + m21*Z) / z
        next
        gosub object3            REM -- Draw sub object 3
      else                       REM -- Face 3/5 => obj 1
        d2 = py(o1(f,3))
        d0=d2*m10+dx : d1=d2*m11+dy : d2=d2*m12+dz
'       --- Project the vertices onto the cube face
        for p=1 to 8
          X = itX(p) * 0.47619
          Z = itY(p) * 0.47619
          z      = (m02*X + d2 + m22*Z) / mult
          ntX(p) = (m00*X + d0 + m20*Z) / z
          ntY(p) = (m01*X + d1 + m21*Z) / z
        next
        gosub cube               REM -- Draw sub object 1
      fi
    fi
  fi
next

if in=0 goto loop REM ====================================

'--- Intro foreground stuff (intro is ON as long as in<>0)
if in<4 then
  if in=1 then
    y = prm
    introX = min( 84, introX+0.51)
    prm = prm+1
    if prm=-154 then prm=0 : in=2 fi
  else
    setrgb 1,255,255,255
    if scry>-400 text 1450+scry*5,250,"The real part starts in a few seconds..."
    y = ddy-154
  fi
  setrgb 1,30,30,50
  fill rect -320,-256 to y,256
  clear fill rect y-4,-256 to y,256
  setrgb 1,200,200,255
  line y-3,-256 to y-3,256
  if in<>1 then
    clear fill rect -300,scry+319 to -180,scry+344
    text -310,scry+750,S$,"lb"
    if scry<-974 then
      if in=2 then in=3 : iz=dz fi
      ddy = ddy-2
      introX=max(0,introX-1.5)
      if y<-320 then  in=4 : prm=0 : S$=""  fi
    elsif scry<-880 then
      introX = introX-0.7
    else
      rect -300,scry+319 to -180,scry+344
      x=-213 : y=scry+556 : gosub iconTriangle
      x=-307 : y=y+17     : gosub iconSquare
    fi
    scry = scry-1
  fi
elsif in<7 then
  y = 260
  if in=6 y=y+450-450*prm
  setrgb 1,0,0,0
  for i=1 to 11
    lineNb = pbl(i,1)
    z = 2 * letter(lineNb,0) + 2
    new curve
    for f=1 to z step 2
      line to letter(lineNb,f+1)-220, y+letter(lineNb,f)
    next
    new curve
    for f=1 to z step 2
      line to 220-letter(lineNb,f+1),-y-letter(lineNb,f)
    next
    y = y-pbl(i,2)
  next
fi

GOTO loop REM ============================================

LABEL cube REM ===========================================
'
' Draws the (cube) sub object #1 (6 faces, 8 vertices).

  for F=1 to 6
    if itP(F)<0 then
      f1=o1(F,1) : f2=o1(F,2) : f3=o1(F,3) : f4=o1(F,4)
      x1=ntX(f1) : y1=ntY(f1)
      x2=ntX(f2) : y2=ntY(f2)
      x3=ntX(f3) : y3=ntY(f3)
      x4=ntX(f4) : y4=ntY(f4)
      c = f+1
      if mode=1 then
'       --- Gouraud shading
        z1=itZ1(f1): z2=itZ1(f2): z3=itZ1(f3): z4=itZ1(f4)
        cr=R(c) : cg=G(c) : cb=B(c)
        setrgb 1, z1*cr, z1*cg, z1*cb
        setrgb 2, z2*cr, z2*cg, z2*cb
        setrgb 3, z3*cr, z3*cg, z3*cb
        gtriangle x1,y1 to x2,y2 to x3,y3
        setrgb 2, z4*cr, z4*cg, z4*cb
        gtriangle x1,y1 to x4,y4 to x3,y3
      else
'       --- Flat shading
        z1=( itZ1(f1)+itZ1(f2)+itZ1(f3)+itZ1(f4) ) / 4
        setrgb 1, z1*R(c), z1*G(c), z1*B(c)
        fill triangle x1,y1 to x2,y2 to x3,y3
        fill triangle x1,y1 to x4,y4 to x3,y3
      fi
'     --- Cel shading
      if cel=1 then
        setrgb 1,0,0,0
        new curve     : line to x1,y1
        line to x2,y2 : line to x3,y3
        line to x4,y4 : line to x1,y1
      fi
    fi
  next

RETURN

LABEL object2 REM ========================================
'
' Draws the sub object #2 (8 faces, 6 vertices).

  for F=1 to 8
    if itP2(F)<0 then
      f1=o2(F,1) : f2=o2(F,2) : f3=o2(F,3)
      x1=ntX(f1) : y1=ntY(f1)
      x2=ntX(f2) : y2=ntY(f2)
      x3=ntX(f3) : y3=ntY(f3)
      c = f+1
      if mode=1 then
'       --- Gouraud shading
        z1=itZ2(f1): z2=itZ2(f2): z3=itZ2(f3)
        cr=R(c) : cg=G(c) : cb=B(c)
        setrgb 1, z1*cr, z1*cg, z1*cb
        setrgb 2, z2*cr, z2*cg, z2*cb
        setrgb 3, z3*cr, z3*cg, z3*cb
        gtriangle x1,y1 to x2,y2 to x3,y3
      else
'       --- Flat shading
        z1=( itZ2(f1)+itZ2(f2)+itZ2(f3) ) / 3
        setrgb 1, z1*R(c), z1*G(c), z1*B(c)
        fill triangle x1,y1 to x2,y2 to x3,y3
      fi
'     --- Cel shading
      if cel=1 clear triangle x1,y1 to x2,y2 to x3,y3
    fi
  next

RETURN

LABEL object3 REM ========================================
'
' Draws the sub object #3 (12 faces, 8 vertices).

  for F=1 to 12
    if itP3(F)<0 then
      f1=o3(F,1) : f2=o3(F,2) : f3=o3(F,3)
      x1=ntX(f1) : y1=ntY(f1)
      x2=ntX(f2) : y2=ntY(f2)
      x3=ntX(f3) : y3=ntY(f3)
      c = f+1
      if mode=1 then
'       --- Gouraud shading
        z1=itZ3(f1): z2=itZ3(f2): z3=itZ3(f3)
        cr=R(c) : cg=G(c) : cb=B(c)
        setrgb 1, z1*cr, z1*cg, z1*cb
        setrgb 2, z2*cr, z2*cg, z2*cb
        setrgb 3, z3*cr, z3*cg, z3*cb
        gtriangle x1,y1 to x2,y2 to x3,y3
      else
'       --- Flat shading
        z1=( itZ3(f1)+itZ3(f2)+itZ3(f3) ) / 3
        setrgb 1, z1*R(c), z1*G(c), z1*B(c)
        fill triangle x1,y1 to x2,y2 to x3,y3
      fi
'     --- Cel shading
      if cel=1 clear triangle x1,y1 to x2,y2 to x3,y3
    fi
  next

RETURN

LABEL matrix REM =========================================
'
' This routine builds the transformation matrix:
' rotation_X . rotation_Y . rotation_Z . homothecy_1/4
' The homothecy in here speeds up the projection computing

  m00 = CY*CZ
  m01 = -CY*SZ
  m02 = SY * 0.25
  m10 = CX*SZ + SX*SY*CZ
  m11 = CX*CZ - SX*SY*SZ
  m12 = SX*CY * -0.25
  m20 = SX*SZ - CX*SY*CZ
  m21 = SX*CZ + CX*SY*SZ
  m22 = CX*CY * 0.25

RETURN

LABEL background REM =====================================
'
' This routine clears the background.
' The background color used depends on the current intro
' step (variable "in").

 if in<4 then
  clear window
 else
  if in=5 then
    setrgb 1, col(1,3,1), col(1,3,2), col(1,3,3)
    fill rect -320,-256 to 320,256
  elsif in<7 then
    setrgb 1, prm*col(1,3,1),prm*col(1,3,2),prm*col(1,3,3)
    fill rect -320,-256 to 320,256
    if in=6 then
      prm = prm - 0.016
      if prm<=0 then prm=0 : in=7 fi
    else
      prm = prm + 0.016
      if prm>=1 then iz=0 : in=5 fi
    fi
  else
    clear window
  fi
 fi

RETURN

LABEL handleKey REM ======================================
'
' Checks gamepad keypress, and adjusts the drawing mode.
' The gamepad is inactive until the intro is over.

  if in=0 then
    x=peek("port1")
    if x<>port then
      port=x
      if port=32768 mode=1-mode  REM -- square   (gouraud)
      if port=4096  cel=1-cel    REM -- triangle (cel)
    fi
  fi

RETURN

LABEL preIntro REM =======================================
'
' Some preprocessing for the intro steps...

  if in<5 dy=dy*1.9
  if in=1 then
    dz=max(dz,dz+iz-0.033)
    iz=iz*0.95
  elsif in=3 or in=4 then
    dz=iz
    iz=iz*1.045
    introX=max(0,introX-0.333)
  elsif in<>2 then
    rX=16*90 : rY=0
    if in=5 then
      dz=max( 1, 50+prm)
      prm=prm-dz/35
      if dz=1 then in=6 : prm=1 fi
    elsif in<7 then
      dz=1
    fi
    if in<7 then
      dx=0 : dy=0 : rZ=0
    elsif in=7 then
      in=8
      if ix=0 if abs(dx)<.002 then ix=1 else dx=0: in=7 fi
      if iy=0 if abs(dy)<.002 then iy=1 else dy=0: in=7 fi
      if iz=0 if dz<1.001     then iz=1 else dz=1: in=7 fi
    elsif in>7 then
      setrgb 1,255,255,255
      if in=8 then
        text 400+prm,-230,"Is that all ???"
        prm=prm-4
        if (prm<-1050) then prm=0 : in=9 fi
      elsif in=9 then
        text 320+prm,-230,"Noooooooooooooooooooo !!!"
        prm=prm-10
        if (prm<-870) then prm=0 : in=10 fi
      elsif dz>1.435 then
        in=0
      fi
    fi
  fi

RETURN

LABEL iconSquare REM =====================================
'
' Draws a "square" key logo at (x,y).

  setrgb 1,255,100,255
  rect x,y-2   to x+10,y-11
  rect x+1,y-3 to x+9 ,y-10
RETURN


LABEL iconTriangle REM ===================================
'
' Draws a "triangle" key logo at (x,y).

  setrgb 1,100,200,100
  fill rect x+4, y-10 to x+6, y-13
  fill rect x+2, y-7  to x+4,  y-11
  fill rect x+6, y-7  to x+8,  y-11
  fill rect x,   y-4  to x+2,  y-8
  fill rect x+8, y-4  to x+10, y-8
  fill rect x-1, y-3  to x+11, y-5

RETURN

'#########################################################
'##
'## That's all folks !!!
'##
'#########################################################





