Gemini
======
version     : 1.13
location    : inside !TopRes folder (!TopRes.GeminiGE.Gemini)
questions to: Giancarlo Castagno (gcastagno@coord3.com)
disabled    : some lens-flares effects and some camera parameters.

 1996-97 Sincronia Soluzioni Multimediali, Italy
  It cannot be used without the written permission of Sincronia

Here follows a description of Gemini, the new TopModels graphic engine.
It is a relocatable module available also to third parties, as it can be shared
across all the applications that need 3D visualisation. It lets users to
display a 3D scene and navigate into it.
Gemini can be run also on older machines (pre RiscPC, without dynamic areas).
Applications that use it will work happily with all its the future versions as well.
Module upgrades will offer automatic file loading and a common wimp
interface (like the Acorn !ColourPicker one).

Gemini main features:
=====================
- Works on any Acorn processor, from ARM2 to StrongArm.
- Phong illumination model and support for Cook&Torrances one.
- Wireframe, flat shading, gouraud shading and phong shading view modes.
- Up to 1024 light sources per scene, of any type (sun, point, spot) and colour, all recalculated real-time.
- Shaded wireframe, for high-speed illuminated rendering; this display style was originally
  created by Sincronia and introduced in TopModel from its very first versions.
- Phisical-based material models (ambient, diffusion, reflection RGB, transparency RGB (average, add and sub)
  and refractive index).
- Full Z-buffer (in any view mode), with a T-Buffer for a correct rendering of transparencies.
- Real-time parametric fog and haze.
- Texture, chrome and bump mapping (with control over height). Gemini supports also several techniques superimposed.
- Lens-flares effects.
- Several camera models.
- Normal and VR navigation modes.
- Scene is always calculated at 96bits per pixel and then reduced using a real-time Floyd/Steinberg
  error diffusion algorithm.
- New enhanced radiosity view mode that deals with real-time soft shadows and light diffusion.
- Possibility to place an image (in any format) in the background; it can follow the rotation
  of the view (like a VRQuickTime).
- Direct import of VRML files (up to 40 nested Separator nodes, support for recursive nested
  DEF-USE nodes, management of lights and navigation nodes).
- Support for animation: hierarchical objects management and a Gemini_Transform call to
  move/scale/rotate them.

Licensing
=========
Gemini can be freely used for non-commercial purpose applications (just let us know what youre
doing ;-). Commercial packages can use Gemini only with our previous authorization: (you will be asked
to pay a symbolic fee and youll receive full technical support and upgrades).

Gemini environment:
===================
Gemini is basically a collection of routines to manage a 3D database and render
it both on the screen or to a memory area (to create a sprite); the code contains
tables of pre-calculated data (1/x, sin(x), 1/SQR(x) and so on). If you need to
know how to use them not to duplicate information (i.e. if you need to use
them too), ask us for the full specs...
All the relevant information on how to render the files are stored within the
application using it. Thats why it can be shared instantly between applications.
Gemini is simple to use, as it only has three main calls: one for initialise itself,
one to set the work-area and one to draw the whole scene. The tricky part of the
system are the memory areas used to make it work. You can simply set them big enough
(go on reading this file), without bother why they are needed. Anyway better if you
understand fully how the whole system works and what are the main memory areas used...

A 3D scene is made of 2 separated database, for speed & memory needs: a points
database, which contains all the points used by the model (X,Y,Z of the point
plus some infos) and a primitives database, that contains the description of
each primitive (polygon, sphere or Bezier line/surface) used. For example, this
is a reduced GEO file which describes a cube that can be used to provide a good
example of how the data bases work:

8                    | total number of points
0 0 0                | points 0: X Y Z
1 0 0                | points 1: X Y Z
1 1 0                | ...
0 1 0                | ...
0 0 1                |
1 0 1                |
1 1 1                |
0 1 1                |
                     |
4 0 1 2 3            | polygon of 4 sides, connecting points 0,1,2,3
4 7 6 5 4            | polygon of 4 sides, connecting points 7,6,5,4
4 0 4 5 1            | ...
...                  | ...

Obviously TopModel files use the memory location of a point (from now on LDM)
instead of its number for speed needs, but basically this is the structure.
It allows to share points between polygons (a cube uses 8 points and 6 polygons,
instead of 24 points (4 for each poly) and 6 polygons).
A TopModel file is basically a memory dump of these two TopModel work areas,
plus some other information (for example the minimum size of each area).
So, before loading a file you have to reserve enough workspace for it. If you
want to speed up things, you can simply reserve a memory area as big as the 3Dfile
size, (plus 2/3 Kbytes for security... ;-). The areas can be stored within your
application workspace or in a dynamic area, and can be two different areas or
just one splitted. Gemini needs only to know where Points and Primitives areas
start in memory.

Please note: Gemini (and TopModel) uses the first 58 points for internal use (axis
and so on); thus you must reserve them (32 bytes each, so 58*32 bytes) at the
start of the Points area. So, once you have dimensioned enough workspace for points,
starting from an ldm stored in the variable PointsLDM, you should load the Points
information of the file at PointsLDM+58*32.

There is another memory area to store the main resources: a table of preset
(plus user defined) colours and materials definition plus a table for textures
and groups setup.

  TopModel (and Gemini) manages 512 colours, (up to 200 preset and up to 311
  user defined). Each colour uses 16 bytes (0-11 for the name and 12-15 for the
  &BBGGRR00 of the colour palette). So you have to reserve a 512*16 bytes for
  the colour table. In the same way TopModel stores also the 256 materials
  (up to 100 predefined and up to 155 user defined). Each material uses 32 bytes
  (0-11 for the name 12-31 for material definition). Thus you have to reserve
  256*32 bytes for the material table. In this area you should also store the
  texture information table. Gemini manages up to 512 textures and each entry in
  table is 16 bytes long, so you have to reserve another 512*16 bytes.
  Texture 0 is reserved to say No texture, so its values in the table should
  always remains 0; the first valid texture entry is n^1 (at the offset +16). The
  entry for each texture are:
    +0 =ldm of the sprite;
    +4 =X dimension of the sprite (pixel);
    +8 =Y dimension of the sprite (pixel);
    +12=2^29 (or colour (at 16bpp) to be made transparent).
  Finally this buffer holds also group data. Gemini allows up to 2048 groups to
  be defined, and each of them uses 8 bytes. Thus this area needs 2048*8 bytes.
  When you call Gemini_Initialise for the first time, it fills this buffer
  with the predefined materials and colours and resets the textures and groups tables.
  To do this, set the bit 1 in the flag word (R4).
  Every TopModel file (&B5C) may contain some colours or materials created by user.
  These should be loaded from the 200th colour (offset +200*16) and from the 100th
  material (offset +100*32). Look at the loading procedure (see later) to see how
  they are loaded. The total size of the area is 40960 bytes (512*16+256*32+512*16+2048*8).

After that you should create a normal sprite area defined in RiscOS, in which
you have to load the textures used, taking them from inside the Resources
folder. In the file you find the path of each texture used inside the
!TopRes.texture directory (the system variable <TopModel$Textures> holds the full path.
You may need to use !ChangeFSI if the textures arent 16bpp RiscOS Sprites, as Gemini
can manage only them; once loaded you should set the correspondent table entry
with the texture ldm and dimensions. If you want to discard textures, simply dont
load them, leaving the texture table filled with 0s.

Then you have to reserve some memory for the Z buffer. The exact amount
depends upon the size of the image to be created. Gemini uses 4 bytes for each pixel
of the image. Last but not least there is the image buffer. You can plot directly
on the screen, and in this case you dont have to dimension nothing, or you may
want to plot to sprite. In this case you need as much memory as the one required
to store a normal RiscOS sprite in the resolution and number of colours you need.

To sum up: you have to set 5or6 work areas:
  1) Points     work-area (the file contains the minimum size)
  2) Primitives work-area (the file contains the minimum size)
  3) Colours+Material+Textures+Groups table (40960 bytes (512*16+256*32+512*16+16384))
  4) Texture sprite area (big enough to load all the 16bpp sprites)
  5) Z buffer (4 bytes for each pixel of the image to be created)
  6) image buffer (if you arent plotting to the screen): the same size of a RiscOS sprite.

Obviously you can store all of them in a single area or split them on several
segments. Note: ALL THEM MUST BE WORD ALIGNED!

Now Gemini is ready to work happily within your application.
There are three main blocks of information to drive Gemini engine.
I call them TMmemoryblock, TMviewblock, TMcolourblock.

  TMmemoryblock (256 bytes)
  -
    it contains the relevant memory information for Gemini. Some of the
    locations are updated, some other are filled with ldm of tables of
    pre-calculated data. These must be filled:

    +0  ldm start of Points workspace
    +8  dimension of Points work-area in bytes
    +12 ldm start of Primitives workspace
    +20 dimension of Primitives work-area in bytes
    +40 ldm start of Z buffer
    +44 ldm colours+materials+textures+groups table


    Other relevant values, set by Gemini_Initialise, when you call it with
    bit 0 (in the flag word) set, are:

    +4  first free location in Points workspace (where to load Points from
        file). Updated when loading the file (or editing it).
    +16 first free location in Primitives workspace (where to load Points
        from file). Updated when loading the file (or editing it).
    When no object is loaded, the default values are:
                            TMmemoryblock!4 =TMmemoryblock!0 +58*32
                            TMmemoryblock!16=TMmemoryblock!12
    +52 ldm of texture table
    +64 ldm of materials table
    +68 ldm of groups table
    These three are used by Gemini to speed up things. Instead of recalculating each time
    the ldm of the start of each area, Gemini simply calculates them once when a
    Gemini_Initialise is called, storing the start ldm of textures, materials and groups
    tables out of the address you pass in TMMemoryblock!44.
    Simply: +44 colours+materials+textures+groups table; this is also the ldm of
                                            the start of colour area:  512*16 bytes
            +52 texture table:       (previous ldm + 512*16); it uses  256*32 bytes
            +64 materials table:     (previous ldm + 256*32); it uses  512*16 bytes
            +68 groups table:        (previous ldm + 512*16); it uses 2048*8  bytes

  TMviewblock (256 bytes)
  -
    it contains all the relevant parameters to drive the shading process. Some
    locations are updated and some others are filled with some infos about the
    current screen modes and so on. For a full list, ask me. The values
    inside [] are the default values set when you call Gemini_Initialise. All
    the values must be integers, so to keep some precision, your numbers are
    multiplied by some factor. To obtain their original value, just take the value
    stored inside the corresponding location and divide it by the multiplier
    factor (eg. if you have 0.5 and a multiplier factor of 2^16, you should
    store the number 2^16*0.5=2^15=32768. When you need to know its value,
    read it and divide it by 2^16, so 2^15/2^16=0.5). You have to alter only the
    values you are interested in.

    +0   Z angle of view *16 (so 0 <= Zangle <= 360*16).To rotate around Z [ 0*16]
    +4   Y angle of view *16 (so 0 <= Yangle <= 360*16).To rotate around Y [90*16]
    +8   0 (for the X angle, but currently unused)                         [ 0*16]
    +12  X position of the observer (*2^17) To pan around                  [ 0*2^17]
    +16  Y position of the observer (*2^17)                                [ 0*2^17]
    +20  Z position of the observer (*2^17)                                [ 0*2^17]
    +24  current OS version (currently unused)
    +28  zoom factor *2^10 ( 1*2^10 =100%)   Note: (0 <= zoom <= 64*2^10)  [ 5*2^10]
    +44  distance observer-plane of projection (*2^17). Used to alter the
         perspective deformation (works just in perspective...)            [10*2^17]
    +100 Linear FOG intensity *2^14 (0 <= intensity <= 4*2^14)             [ 1*2^14]
    +104 Z coordinates at which the fog starts (*2^17).                    [ 0*2^17]
    +108 Z intensity of the fog *2^14  (0 <= height <= 4*2^14)             [ 1*2^14]
    +112 Sun light X direction (the X component of the normal)             [ 0*2^15]
    +116 Sun light Y direction                                             [ 0*2^15]
    +120 Sun light Z direction                                             [ 1*2^15]
    +124 Sun light intensity       (0..1 *2^16)                            [ 1*2^16]
    +128 Sun light colour          (&BBGGRR00)                             [&FFFFFF00]
    +132 Ambiental light intensity (0..1 *2^16)                            [ 0*2^16]
    +136 Ambiental light colour    (&BBGGRR00)                             [&FFFFFF00]
    +140 Illumination has changed flag                                   [255    ]
    +144 Gemini_version set in Gemini_Initialise                           [110    ]
    +156 Colours have changed flag                                       [255    ]
    +172 precision in drawing a bezier curve        (1=high...6=low)       [3      ]
    +176 precision in drawing a bezier surface      (1=high...6=low)       [5      ]
    +192 Geometry has changed flag                                       [0      ]
    +200 bits 0 -8 : texture used for picture in background (0=none)       [0      ]
         bit  9    : set to 1 if texture must rotate with the view         [0      ]
         bits 10-20: X offset on screen                                    [0      ]
         bits 21-31: Y offset on screen                                    [0      ]
    +204 Parametric grid: step X (*2^17)                                   [ 4*2^17]
    +208 Parametric grid: step Y (*2^17)                                   [ 4*2^17]
    +212 Parametric grid: step Z (*2^17)                                   [ 4*2^17]
    +216 Parametric grid: number of points for each side                   [50     ]
    +220 Parametric grid: centre X (*2^17)                                 [ 0*2^17]
    +224 Parametric grid: centre Y (*2^17)                                 [ 0*2^17]
    +228 Parametric grid: centre Z (*2^17)                                 [ 0*2^17]

  Note: set the three flags (+140,+156,+192) to tell Gemini to recalculate the
        correspondent value: +140 (illumination) if you have changed front light
        or you have moved the lights in the scene; +156 (colours) if you have
        changed some values in TMcolour block; +192 (geometry) if you want to
        recalculate all the normals of points and primitives in the scene
        (for example if you have deformed some objects or you simply want to
        calculate normals of objects you have created yourself). Gemini will
        recalculate the correspondent values next time you issue a Gemini_Render
        call, then it will reset them to 0 (you need to calculate those things
        just once).

  TMcolourblock (256+1024*4 bytes)
    it contains all the colours used in the Gemini environment. They are stored
    separately, because Gemini recalculates them each time a user change
    graphic mode. The 1024*4 additional bytes are used to store a table of user
    defined lights. See later. Each colour entry uses 8 bytes; 4 for &BBGGRR00
    of the colour and the second 4bytes to store the translated colour for the
    current graphic mode. The values inside [] are the default values set when
    you call Gemini_Initialise.
    Once Gemini is running, if you want to change a colour (say the fog colour)
    just alter the correspondent location with the new palette entry and then
    inform Gemini that you have changed colour setting TMviewblock!156 to a
    value different from 0. Gemini will recalculate the new colour table next
    time a Gemini_Render call is issued and resets this location (+156) on exit.

    +0   Background colour (or sky start,for shaded background)    [&AA999900]
    +8   Active plane colour                                       [&00FFFF00]
    +16  Active axis colour                                        [&0000FF00]
    +24  Fog colour                                                [&AA999900]
    +32  Sky end colour      (shaded background - disabled)        [&FF000000]
    +40  Ground start colour (shaded background - disabled)        [&BF444400]
    +48  Ground end colour   (shaded background - disabled)        [&00FF2200]
    +56  Bezier visual-hull lines colour                           [&FFD53B00]
    +64  Polygons vertices colour                                 [&00FFA000]
    +72  Bezier control-points colour                              [&FF000000]
    +80  Selected points colour                                    [&0000FF00]
    +88  Lattice deformation   (used in TopModel editor)           [&00FFFF00]
    +96  Colour of active path (used in TopModel editor)           [&00FFA000]
    +104 Colour of grid        (mainly used in TopModel editor)    [&FFFFFF00]


Please note: Gemini, apart from the lights stored within a TopModel file, works
 with two ever present lights: a general sun light and an ambiental one (as you
 see from the structure of TMviewblock). To disable them just set their
 intensity to 0. If you want to light the scene frontally, you should set the
 sun beam direction (TMviewblock!112,116,120) to the values contained in
 TMviewblock!48,52,56 respectively, which hold the current view vector. After
 this you should inform Gemini that illumination has changed, setting
 TMviewblock!140 to a value different from 0. In this way, next time you issue
 a Gemini_Render call, Gemini will recalculate illumination before drawing the
 scene. Gemini will automatically reset this location to 0 on exit.


Now lets come to the Gemini calls.
First of all you must initialise your work-areas (TMmemory and view blocks):

SYSGemini_Initialise

R0 = TMmemoryblock
R1 = TMviewblock
R2 = TMcolourblock
R4 = flags:  Bit   Meaning if set
            -
              0  | Initialise Points&Primitives work-area and resets textures and groups
              1  | Reset colour&material table loading the default colours and materials
              2  | Reset TMviewblock & TMcolourblock entries to their default values
            3-31 | reserved - must be 0

On exit, this call also returns the Gemini version number*100, in TMviewblock!144

The first time you call Gemini_Initialise you should set R4 to 7 (set bits 0-2)
This loads the colour and material default definitions and clear the scene, filling
with 0s the texture table and setting the right values in the first 58 points.
It also updates the Points and Primitives first free ldm (TMmemory+4 and +16).
In this way, when you issue a Gemini_Render call, after this, Gemini will display
just the axis in the centre of the work-area.
Then you can call Gemini_Initialise each time you need to reset your scene (delete
the objects inside) setting R4 to 1.




SYSGemini_InitScene
-
When you have loaded a file you have to adjust all its internal links (i.e. the
ldm of the points used by the polygons); you should call this from inside the loader
code (see later).

R1 = offset to add to the points
       [(current location at which points are loaded in memory)-(the old location
         from where they were saved, stored in the file)]
R2 = offset to add to the primitives
       [idem]
R3 = location at which points     are loaded (TMmemoryblock!4 , mostly)
R4 = location at which primitives are loaded (TMmemoryblock!16, mostly)




SYSGemini_SetUpLights
-
After loading a file, you should compile a light table, to access quickly to
the light information. The lights (up to 1024 per scene) are memorized in a
table (of 1024*4 bytes) within the TMcolour block. You only have to dimension
correctly the TMcolourblock and call this routine, which scans the Point database
(in which lights are stored) and compile a table of ldm of the lights in memory.
Then recalculates also the illumination of Points and primitives...

entry:
R0 = TMmemoryblock
R1 = TMviewblock
R2 = TMcolourblock
exit:
R4 = n^ of lights found

||| If you want to move your scene in memory, once you have loaded and used it,
||| just move physically the memory, update the links within TMmemoryblock
||| (+0 to +20), call Gemini_InitScene with the new parameters; then
||| call Gemini_SetUpLights.




Then you must specify the dimension of the work area. You must call these
routine every time (1) you want to change the image size or
                   (2) a change of the desktop palette or of the graphic mode
                       has occurred.
There are two routines, which let you specify if you want to work on the screen
or in a memory buffer (you have to call only one of these):

(a) SYSGemini_SetUpScreen
    -
    This allows you to output the scene on the screen. You have to set:
    R0 = TMmemoryblock
    R1 = TMviewblock
    R2 = TMcolourblock
    R3 = X min work-area (OS units, inclusive)
    R4 = Y min work-area (OS units, inclusive)
    R5 = X max work-area (OS units, inclusive)
    R6 = Y max work-area (OS units, inclusive)
    R7 =   0 (for a complete setup, which recalculates also the colour tables for the current palette)
         <>0 (window/work-area resize only, much faster)

(b) SYSGemini_SetUpSprite
    -
    This allows you to output the scene on a sprite. You must set:
    R0 = TMmemoryblock
    R1 = TMviewblock
    R2 = TMcolourblock
    R3 = sprite width  (in pixels)  Please note that the sprite width will be corrected:
                                     16 colours mode: rounded to a multiple of 8 pixels
                                    256 colours mode: rounded to a multiple of 4 pixels
                                    32K colours mode: rounded to a multiple of 2 pixels
                                    16M colours mode: all values are accepted
    R4 = sprite height (in pixels)
    R5 = log2 bit/pixel (2=16colours  3=256colours  4=32Kcolours  5=16Mcolours)
    R6 = pointer to control block of sprite area (it is reinitialized and must contain
         (R3 * R4 * 2^R5 / 8 + 128)  bytes at least)


On exit, both these calls fill in the TMviewblock with some information. In
particular (in TMviewblock):
  +72 X min work-area (OS units)
  +76 Y min work-area (OS units)
  +80 X max work-area (OS units)
  +84 Y max work-area (OS units)
these may or may not be equal to those you pass as input. Coordinates are
translated into memory addresses for plotting purposes, which MUST be byte
aligned. In practice, only the X coordinates may be affected from this
readjustment phase. In particular, when in a 4bpp mode, X work-area coordinates
must be multiple of 2 pixels. From 8bpp modes upwards (and so also 16bpp and 32bpp)
there arent restrictions.

So: call Gemini_SetUpScreen (or _SetUpSprite) specifying your work-area, then
    check which are the settings Gemini assumes to be right and use them
    instead. Please NOTE: Gemini DOESNT WORK in any 2 or 4 colours mode. If a
    user enters these graphic modes, Gemini fill these addresses (+72 to +84)
    with a particular value: -1. You dont have to check the graphic mode specs
    on a mode change. Just call Gemini_SetUpScreen (or _SetUpSprite) as usual.
    If you get, on exit, the -1 value in the previous locations, then warn the
    user that Gemini doesnt work in this mode and close all the windows you
    need (or fill them with a dummy background or use ChangeFSI to convert the
    last image Gemini produces in one suitable for that graphic mode).
In any case dont call Gemini_Render while in a wrong graphic mode, as the
result will be unpredictable.




And, (at last!), the call to render all the scene on the specify output stream
(video or memory) in the specified size...

SYSGemini_Render

R0 = TMmemoryblock
R1 = TMviewblock
R2 = TMcolourblock
R3 = screen bank to use (1 or 2; 1 for default)
R4 = mode

Gemini enables you to have a full double buffering for smooth animation. Just
redraw the screen with R3 set to 1 or 2 alternatively. (see later)
The R4 register contains the mode word. These are the actual defined bits:

 Bit   Meaning if set   [meaning if not set]

   0  | Colour [grey-shade]
   1  | Wireframe
   2  | Flat shading
   3  | Gouraud shading
   4  | Phong shading
   5  | Transparency enabled  [always opaque]
   6  | Texture mapping enabled
   7  | Chrome  mapping enabled
   8  | Bump    mapping enabled
 9-11 | reserved - set to 0
   12 | draw primitives [dont draw them]
   13 | reserved - set to 0
   14 | perspective view [axonometric view]
   15 | normal navigation mode [VR mode: objects behind observer arent drawn]
   16 | visualize axes [dont]
   17 | recalculate active plane while rotating [dont]
   18 | remove objects with normals against the current view vector [draw all]
   19 | test ESC key to stop redraw [dont]
   20 | enable fog [no fog]
   21 | front light on (Gemini recalculates the direction of the default sun light
        to match the current view). This causes also re-calculation of light
        reflections. [fixed sun light]
   22 | recalculate light reflections if you arent in Phong mode [dont]
   23 | clear the work-area with the current background colour [draw just objects]
   24 | draw bezier-curves/surfaces visual-hull lines [dont]
   25 | draw polygons vertices   [dont]
   26 | draw control points       [dont]
   27 | enable lens-flares        [dont]
   28 | show grid points          [dont]       -> not activated yet
   29 | visualise lights position [dont]
   30 | make selected object(s) darker and always draw selected points,
        disregarding settings of bits 25/26/29 (which remain valid for
        un-selected points)       [dont emphasize selection]
   31 | reserved - set to 0

Please note: bits 1-4 are mutually exclusive; you should set just one of these.
             Nevertheless you can set Flat+Gouraud+Phong shading (so bits 2-4=%111)
             to obtain the radiosity view-mode. But at the moment it is for internal
             use only.




Then there are another two calls, to enable screen buffering

SYSGemini_StartBuffering

R1 = TMviewblock

This tries to reserve enough video memory to obtain a second bank to enable
screen banking for smooth animations. If it hits the goal, then it copies the
first page into the second, to enable a bank switching also from desktop (the
second page isnt filled with strange colours this way...). Otherwise nothing
happens and Gemini will use the current video memory.

SYSGemini_EndBuffering

R1 = TMviewblock

This ends the bank switching, releasing (if any) the reserved memory and
copying the contents of the second page (if the current page is the second)
into the first.

So, to have a working engine with bank switching do this:
Gemini_StartBuffering:bank%=1
REPEAT
  bank%=bank%EOR3:Gemini_Render,... ,... ,... ,bank% ,...
UNTIL end
Gemini_EndBuffering

this work also if there isnt enough memory for bank switching.




Operations over loaded objects
==============================
SYSGemini_Transform
-
This call allows you to move/rotate/scale objects. It works over selected objects (so
you first have to select something) but it can affect numbered groups only (both
selected or un-selected).
R0 = TMmemoryblock
R2 = objects to alter:
     0 > modify selected objects (you have to select something first)
     n > logical number of the group to be modified (no need to select it)
R4 = reason code:
     0 > translate objects/group
     1 > scale objects/group
     2 > transform objects/group using a general transformation matrix
R5 = memory block filled with geometric data for the operation (all the values are
     multiplied by 2^17, i.e.: original value=0.5, value to store=0.5*2^17):

offset |  translate                |      scale       |         general transform
-|-||-
+0     | amount to add to X coord. | X coord. of the point around which the operation takes place
+4     | amount to add to Y coord. | Y coord. of the point around which the operation takes place
+8     | amount to add to Z coord. | Z coord. of the point around which the operation takes place
+12    |     -                     | X scale factor   | A11 component of the transformation matrix
+16    |     -                     | Y scale factor   | A21
+20    |     -                     | Z scale factor   | A31
+24    |     -                     |     -            |     A12
+28    |     -                     |     -            |     A22
+32    |     -                     |     -            |     A32
+36    |     -                     |     -            |         A13
+40    |     -                     |     -            |         A23
+44    |     -                     |     -            |         A33
+48    |     -                     |     -            | amount to add to X coord.
+52    |     -                     |     -            | amount to add to Y coord.
+56    |     -                     |     -            | amount to add to Z coord.

The general transformation acts as follows: first of all the object is modified accordingly to
the 3x3 matrix (around the specified point), then it is translated by the amount specified in
(+48,+52,+56)




SYSGemini_Select

This is a multi-purpose selection command.
Its behaviour depends upon the reason code passed in R4, as follow:

R4 values  | action taken
-|-
    1      |  Select       all/only visible objects (the ones inside working area)
    2      |  Select       the specified group of objects
    3      |  Select       all the points inside the specified rectangle
    4      |  Find         the point closest to the passed (X,Y) screen coordinates
    6      |  Find         the primitive closest to the passed (X,Y) screen coordinates
    16     |  Deselect     all/only visible objects
    17     |  Deselect     the specified group of objects
    18     |  Deselect     all the points inside the specified rectangle


SYSGemini_Select R4=6
-
R0 = TMmemoryblock
R1 = X (OS-Units relative to the whole screen, eg.: X mouse coordinate)
R2 = Y (as above)
R3 = flag: if bit0 is set, then Gemini will check only visible primitives
R4 = 6

The other calls arent implemented yet




MISC
====
SYSGemini_readVRML

This call loads a VRML 1.1 file whose name (completed with its path,
otherwise in the current directory) is given in R6.

entry:
R0 = TMmemoryblock
R1 = TMviewblock
R2 = TMcolourblock
R3 = U number of polygons for the sphere
R4 = V number of polygons for the sphere
R5 = Number of sides for cone/cylinders base
R6 = bits  0-15: number of user-defined materials
            16 : set to resize the scene to a default dimension
            17 : set to triangularize all the polygons (if the scene contains convex polygons)
R7 = pointer to NULL terminated filename of VRML file (with full path)
exit:
R2 =   0 OK
     <>0 some objects have not been loaded due to lack of memory.
R3 = number of created points
R4 = number of created polygons
R5 = number of created user-defined materials


How to draw the scene in several passes
=======================================
You may need to produce a picture in more than one go, especially if you want
big resolutions (a 2048*2048 32bpp image, with Z-buffer requires 32Mb free!).
You also may want to draw only a portion of the scene required for example by
a Wimp Redraw Request. I recommend you to use this technique only for file or
sprite output, as it slows down the rendering time; anyway it works also
if you specify the work-area using Gemini_SetUpScreen. The basic idea is quite
similar to the one used by the wimp scroll-bars. When you specify the work-area,
Gemini assumes that the origin is placed in the top left corner, and the Y
coordinate is positive downwards. To place the origin at the centre of the work-
area, Gemini simply adds a fixed amount to each coordinate. These amounts are
the centre of the scene (in pixel); lets call them (fx,fy). They are stored
in TMviewblock+32 and +36, respectively. These locations are filled each time
you issue a Gemini_SetUpScreen or Gemini_SetUpSprite call. (fx,fy) are set
to the centre of the currently specified work-area on exit (i.e. if you specify
a work-area of (20,20,120,120) OS-Units (with a X and Y scale factor of 2), on
exit from Gemini_SetUpS... you get (fx,fy)=(25,25), as the work-area is 50x50).
Lets create a 600x384 pixels image, using 3 strips of 600x128 pixels each.
Lets take a scale factor of 2 on both X and Y (eg. MODE28).
Please note: produce strips starting from the top left one to the bottom
right. In the following example I consider only 3 horizontal strips, but
once you have understood how it works, you can redraw any portion inside the
main (logical) work-area.

In OS-Units, here are the 3 work-areas to specify:

  ________________________________________________________768
  |                                                      |
  |                                                      |
  |                                                      |
  |              Strip #1                                |
  |                                                      |
  |                                                      |
  |                                                      |
  |______________________________________________________|512
  |                                                      |
  |                                                      |
  |                                                      |
  |              Strip #2                                |
  |                                                      |
  |                                                      |
  |                                                      |
  |______________________________________________________|256
  |                                                      |
  |                                                      |
  |                                                      |
  |              Strip #3                                |
  |                                                      |
  |                                                      |
  |______________________________________________________|0
 0                                                   1200

So, we have to:
1) Gemini_SetUpS... specifying strip#1
2)  alter the (fx,fy) settings like using scroll bars (see later)
3) Gemini_Render to draw this strip
4) Gemini_SetUpS... specifying strip#2
5)  alter the (fx,fy) settings
6) Gemini_Render to draw this strip
7) Gemini_SetUpS... specifying strip#3
8) alter the (fx,fy) settings
9) Gemini_Render to draw this strip
Each strip, once defined, will have its local coordinate system, starting
from its top-left corner. So, in pixel:
  ________________________________________________________
  |(0,0)                                                 |
  |                                                      |
  |                                                      |
  |              Strip #1                                |
  |                                                      |
  |                                                      |
  |                                            (599,127) |
  |______________________________________________________|
  |(0,0)                                                 |
  |                                                      |
  |                                                      |
  |              Strip #2                                |
  |                                                      |
  |                                                      |
  |                                            (599,127) |
  |______________________________________________________|
  |(0,0)                                                 |
  |                                                      |
  |                                                      |
  |              Strip #3                                |
  |                                                      |
  |                                            (599,127) |
  |______________________________________________________|

the centre of the whole scene MUST BE THE SAME for all the strips, considering
their relative positions. In OS-Units, considering the global image, (fx,fy)
IS POSITIONED to the middle of the second strip (600,384), but when you specify
and work inside a strip, you have to translate the global position of the
logical centre (OS-Unit) in the current local coordinate system of the strip
(pixel).
So, the strip#1 MUST have (fx,fy) set manually to (300,192)
    the strip#2 MUST have (fx,fy) set manually to (300, 64)
    the strip#3 MUST have (fx,fy) set manually to (300,-64)

   ______________________________________________________
  |(0,0)                |                                |
  |      Strip#1        |                                |
  |                     |                                |
  |                     |128+64                          |
  |                     |                                |
  |                     |                                |
  |                     |                      (599,127) |
  |_____________________|________________________________|
  |(0,0)           |    |                                |
  |      Strip#2   |64  |                                |
  |                |    |                                |
  |       300      |    |                                |
  |<>. (fx,fy)                     |
  |                   |                                  |
  |                   |                        (599,127) |
  |___________________|__________________________________|
  |(0,0)              |-64                               |
  |      Strip#3                                         |
  |                                                      |
  |                                                      |
  |                                                      |
  |                                            (599,127) |
  |______________________________________________________|

We are altering only Y position of each strips, so the fx coordinate is
unchanged, equal to half the Xsize of the total image in pixel (i.e. 300).
In general, to generate a portion of the main image, you have to work out
both the coordinates (fx,fy).




Restrictions
============
1) Gemini DOESNT WORK in any 2 or 4 colours mode.
2) Work-area coordinates are translated into memory addresses for plotting purposes, which
   MUST BE BYTE aligned. This involves just the routine that clears the background, not the
   render routine used for objects. Anyway, even if you dont want to redraw background (bit
   23 of R4 in Gemini_Render clear), work-area position is moved, so all the objects inside
   are moved accordingly. In practice, only the X coordinates may be affected from this
   readjustment. In particular, when in a 4bpp mode, X work-area coordinates must be multiple
   of 2 pixels. From 8bpp modes on (and so also 16bpp and 32bpp) there arent restrictions.
3) ALL MEMORY LOCATIONS (Points&Primitives work-area, Zbuffer, TMmemoryblock, .... etc.)
   MUST BE WORD ALIGNED!
4) When drawing to screen, the work-area you specify is always clipped to fit into it:
         ________________                            ________________
        |     screen     |                          |     screen     |
     ___|__________      |                          |__________      |
    |   |          |     |                          |          |     |
    |   |          |     |         ====>>>          | work-area|     |
    |   |          |     |                          |          |     |
    |   |__________|_____|                          |__________|_____|
    | workarea     |
    |______________|




TopModel 3D scene
=================
In a TopModel 3Dscene (&139) basically you can find (raw by raw)

3DScene                           | Header
2.00                              | file version number
27185169                          | mode as described in Gemini_Render (in R4)
48896                             | min size of points     workarea needed (+58*32 bytes always)
85056                             | min size of primitives workarea needed
0                                 | min size of group data needed
24576                             | min size of mats+cols+textures
122000                            | min size of a standard RiscOS sprite-area to load the textures used
256                               | size of the portion of TMviewblock saved
0,0                               | background,fog colours (&BBGGRR00,&BBGGRR00)
0                                 | reserved, must be 0
0                                 | reserved, must be 0
55232                             | ldm of points workarea when it was saved
333376                            | ldm of primit.workarea when it was saved
1680832                           | ldm of texture sprite-area when it was saved

Then you find a memory dump of points, primitives and groups work-areas,
colours+materials+textures table, sprite-file containing the texture and TMviewblock area.




TopModel 3D file
================
In a TopModel 3DFile (&B5C) basically you can find (raw by raw)

3DModel                           | Header
2.00                              | file version number
View1  0,403520,-63204,[...]      | view infos (optional, may be present or not)
Ambiental  255,255,255, 0         | ambiental light infos    (optional)
Solar  255,255,255,    [...]      | standard sun light infos (optional)
Grid  50,4.0000,4.0000 [...]      | grid infos               (optional)
127392                            | min size of points     workarea needed (+58*32 bytes always)
284992                            | min size of primitives workarea needed
0                                 | min size of group data needed
3980                              | total number of points and lights
7124                              | total number pf primitives in the file
3980                              | number of selected points when saved
7124                              | number of selected primitives when saved
0                                 | number of selected groups when saved
0                                 | number of selected lights when saved
981456                            | ldm of points workarea when it was saved
1113392                           | ldm of primit.workarea when it was saved
1                                 | n^ of textures used
1 -536870912 ,Materials.ceramic.2 | (text.number) (transparent colour), path
1 , 1                             | n^ of user defined colours, materials
 Colour1,15658507                 | (colour name),(&BBGGRR00)
 Material1, 194841272,0,0,0,0     | (material name),mat!12,mat!16,mat!20,mat!24,mat!28 (the latter is currently unused)

Then the file goes on with a memory dump of the points workarea followed
immediately by a memory dump of the primitives and of the groups work-areas.




Disclaimer
==========
The module and this documentation are supplied as is; neither Sincronia nor
the author of Gemini make any warranty, whether express or implied, as to the
merchantability of the software or its fitness for any purpose.
In no circumstances will Sincronia or the author of Gemini be liable for
any damage, loss of profits, goodwill or for any indirect or inconsequential
loss arising from the use of this software or of this documentation.
Using Gemini is deemed acceptance of these terms.

