LABTITLE=: 'OpenGL Utilities'
LABFOCUS=: 0

NB. =========================================================
Lab Section Introduction
OpenGL is available in the Windows JFE and in the Java JFE under Linux and Mac OS X.

The next section tests to see if the OpenGL dlls are available.
)
PREPARE
clear''
JUMPS_lab_=: 1

testdlls=: 3 : 0
0!:0 <jpath '~system\extras\util\testogl.ijs'
'All OK.'
)
PREPARE

NB. =========================================================
Lab Section Test dlls
)
testdlls''

NB. =========================================================
Lab Section Getting Started
The basic ideas of the J interface to OpenGL are described in the lab "OpenGL Introduction", which you should have taken first.

This lab introduces the utilities provided, that should make it easier to program OpenGL.
)

NB. =========================================================
Lab Section
First, load the OpenGL utilities. This loads the jgl3 and jzopengl classes and creates public definitions in the z locale.

Normally, you would call the utilities in an jzopengl object. However, to simplify the calls, the jgl3 and jzopengl classes are put on the base path.
)
load 'opengl'
coinsert 'jgl3 jzopengl'
load 'trig'

NB. =========================================================
Lab Section
Books on OpenGL typically contain example code in C that, for the most part, can be copied directly into J with only minor changes.

Much of this code is a wrapper that creates the OpenGL control, and handles the events to move, resize and repaint it. The J system and OpenGL utilities do most of this for you automatically.

The rest of the code are OpenGL commands to create the graphic. You have to generate exactly the same OpenGL commands from J, though you can often take advantage of J's array processing capabilities to simplify the code.
)

NB. =========================================================
Lab Section Display Lists
A key OpenGL feature for performance is the "display list".

A display list is a set of commands that have been "compiled" and given an integer reference number.

When the drawing is painted, the display list can be called in a single step, and the commands execute more efficiently than if they called again from scratch.

You should always use display lists to create a graphic.
)

NB. =========================================================
Lab Section
Typically, you need the following steps to create an OpenGL graphic:

  create a form and isigraph control
  run standard display lists to handle control events
  create one or more new display lists to draw the graphic
  draw the graphic to the new display lists
  issue a request to paint the control

Lets look at a simple example of these.
)

NB. =========================================================
Lab Section glinit
Use "glinit" to create the form and isigraph control. The default parent form name is "opengl", the control name is "g".

The following section runs glinit. This will display the form, which you can move and resize as you wish. Leave it visible for the next step.
)

NB. =========================================================
Lab Section
)
glinit ''

NB. =========================================================
Lab Section stdlist
Use "stdlist" to initialize display lists to translate and rotate the object.

(The display will not change.)
)
PREPARE
wd 'psel opengl'
PREPARE
stdlist''

NB. =========================================================
Lab Section genlist
Create a new display list for the graphic.
)
PREPARE
wd 'psel opengl'
PREPARE
genlist 'BOX'

NB. =========================================================
Lab Section makelist
Draw a solid, i.e. it will not be visible yet:
)
PREPARE
wd 'psel opengl'
PREPARE
drawdodecahedron makelist BOX ''

NB. =========================================================
Lab Section
At this stage, you will have drawn the object - but it is not yet visible. You can make it visible by simply resizing the control or clicking on it if it is partially hidden - try doing so now!
)

NB. =========================================================
Lab Section glpaintx
What has happened is that you have created display lists that will draw the graphic, and also event handlers that respond to events by calling those display lists. The "resize" event is one such event.

In general, you can initiate the display by generating a "repaint" event directly, using glpaintx.
)
PREPARE
wd 'psel opengl'
PREPARE
glpaintx''

NB. =========================================================
Lab Section movement
The graphic will respond to movement keys. In this lab, these are the movement keys:

  i o          move in/out | Shift = move 5 steps in/out

  arrow keys   fly around object
  PageUp,Dn    spin around

  x y z        rotate x y z | Shift = rotate back
  F5 F6 F7     rotate x y z 90 degrees | Shift = rotate back
  Ctrl F5-F7   rotate x y z 180 degrees

  j k l        translate object | Shift = translate back

  F4           reset

Click on the graphics control to give it focus, then try pressing the movement keys
)

NB. =========================================================
Lab Section
We can now define a verb to create the graphic:
)
demo1=: verb define
glinit''
stdlist''
genlist 'BOX'
drawdodecahedron makelist BOX ''
glpaintx''
)

NB. =========================================================
Lab Section
Close down the graphic window, i.e. press Esc when it has the focus.
)

NB. =========================================================
Lab Section
Now run the new verb.

Note that the colors will likely be different. In general, the utilities chose colors at random unless specifically given.

Try running demo1 again.
)
demo1''

NB. =========================================================
Lab Section
"genlist" creates a new list item as an integer. In the previous case, this was the variable BOX. If you want to redefine a list item that you have created, simply regenerate the item using that item number.

If you do not want to redefine that list item, you need not name the list item, and can simplify the code as below. Compare:

NB. named item BOX:
   drawdodecahedron makelist BOX ''

NB. no-name item:
   drawdodecahedron makenewlist ''
)
demo2=: verb define
glinit''
stdlist''
drawdodecahedron makenewlist ''
glpaintx''
)

NB. =========================================================
Lab Section
)
demo2''

NB. =========================================================
Lab Section Lighting
Here is another example. In this case, the cube is drawn in a single color, and lighting is turned on so that the edges can be seen.
)
demo3=: verb define
glinit''
stdlistlight''
drawcube makenewlist SKYBLUE
glpaintx''
)

NB. =========================================================
Lab Section
Run it:

Rotate the cube using the movement keys.
)
demo3''

NB. =========================================================
Lab Section Viewbox and Axes
The standard viewing box used is a cube of edge size 2 units. The utilities drawviewbox and drawaxes help you visualize the viewbox.

This viewbox is a guide only, and you can draw outside of the viewbox if you wish.

Leave demo3 open (rerun it if necessary) and advance to the next lab section.
)

NB. =========================================================
Lab Section
Rotate the graphic using the movement keys.
)
PREPARE
wd 'psel opengl'
PREPARE
genlist 'VIEW'
drawviewbox makelist VIEW WHITE
glpaintx''

NB. =========================================================
Lab Section
Replace with drawaxes and repaint.

Note that re-using the item number VIEW replaces its definition.
)
PREPARE
wd 'psel opengl'
PREPARE
drawaxes makelist VIEW RED
glpaintx''

NB. =========================================================
Lab Section Translation and Rotation
Nouns TRNXYZ and ROTXYZ are 3-element lists specifing the current translation and rotation. You can set these to draw a graphic in a specific position and orientation.

The utility gldefault will reset all such globals to default values.
)
demo4=: verb define
glinit''
gldefault''
ROTXYZ=: 45 45 0
TRNXYZ=: 0 1 0
stdlist''
drawcube makenewlist ''
glpaintx''
)
demo4''

NB. =========================================================
Lab Section
To draw two or more objects, position one object as already described, then use OpenGL calls to store the current position on the stack, translate and rotate to a new position, draw the next object, and pop the stack to complete.

For example:
)
demo5=: verb define
glinit''
stdlist''
drawcube makenewlist ''
startnewlist''
glPushMatrix''
glTranslate 1 _1 0
glRotate 1 45 0 0
drawtetrahedron makenewlist ''
glPopMatrix''
glpaintx''
)

NB. =========================================================
Lab Section
Run demo5. Press the movement keys and note that the objects are linked together.
)
demo5''

NB. =========================================================
Lab Section 3D Surface
"drawsurface" draws a 3D surface, given a 3D array of x,y,z values, where the last dimension has shape 3.

"makexyz" creates a 3D array from a boxed list of x, y and z values, and "fit11" fits them into a box of unit radius.

For example:
)
DAT=: 1.25 * fit11 makexyz (];];sin@*/~) steps _2 2 20

demo6=: 3 : 0
gldefault''
ROTXYZ=: _55 0 _25
COLOR=: PALEGREEN
CLEARCOLOR=: SKYBLUE
glinit''
stdlistlight''
2 drawsurface makenewlist DAT
glpaintx''
)

NB. =========================================================
Lab Section
Run it.
)
demo6''

NB. =========================================================
Lab Section End
For more examples, see the demos in Studio|Demos...|opengl.

When running the demos, select Options|View Definition to see the definitions, and to experiment with changes to the definitions.
)
