Logo Search packages:      
Sourcecode: blender version File versions

GHOST_Test.cpp

/**
 * $Id: GHOST_Test.cpp,v 1.6 2003/01/27 21:35:26 maarten Exp $
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

/**

 * $Id: GHOST_Test.cpp,v 1.6 2003/01/27 21:35:26 maarten Exp $
 * Copyright (C) 2001 NaN Technologies B.V.
 * Simple test file for the GHOST library.
 * The OpenGL gear code is taken from the Qt sample code which,
 * in turn, is probably taken from somewhere as well.
 * @author  Maarten Gribnau
 * @date    May 31, 2001
 * Stereo code by Raymond de Vries, januari 2002
 */

#include <iostream>
#include <math.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(WIN32) || defined(__APPLE__)
      #ifdef WIN32
            #include <windows.h>
            #include <atlbase.h>

            #include <GL/gl.h>
      #else // WIN32
            // __APPLE__ is defined
            #include <AGL/gl.h>
      #endif // WIN32
#else // defined(WIN32) || defined(__APPLE__)
      #include <GL/gl.h>
#endif // defined(WIN32) || defined(__APPLE__)

#include "STR_String.h"
#include "GHOST_Rect.h"

#include "GHOST_ISystem.h"
#include "GHOST_IEvent.h"
#include "GHOST_IEventConsumer.h"


#define LEFT_EYE  0
#define RIGHT_EYE 1

static bool nVidiaWindows;  // very dirty but hey, it's for testing only

static void gearsTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time);

static class Application* fApp;
static GLfloat view_rotx=20.0, view_roty=30.0, view_rotz=0.0;
static GLfloat fAngle = 0.0;
static GHOST_ISystem* fSystem = 0;


void StereoProjection(float left, float right, float bottom, float top, float nearplane, float farplane,
            float zero_plane, float dist,
            float eye);


static void testTimerProc(GHOST_ITimerTask* /*task*/, GHOST_TUns64 time)
{
      std::cout << "timer1, time=" << (int)time << "\n";
}


static void gearGL(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, GLint teeth, GLfloat tooth_depth)
{
      GLint i;
      GLfloat r0, r1, r2;
      GLfloat angle, da;
      GLfloat u, v, len;

      r0 = inner_radius;
      r1 = outer_radius - tooth_depth/2.0;
      r2 = outer_radius + tooth_depth/2.0;

      const double pi = 3.14159264;
      da = 2.0*pi / teeth / 4.0;

      glShadeModel(GL_FLAT);
      glNormal3f(0.0, 0.0, 1.0);

      /* draw front face */
      glBegin(GL_QUAD_STRIP);
      for (i=0;i<=teeth;i++) {
            angle = i * 2.0*pi / teeth;
            glVertex3f(r0*cos(angle), r0*sin(angle), width*0.5);
            glVertex3f(r1*cos(angle), r1*sin(angle), width*0.5);
            glVertex3f(r0*cos(angle), r0*sin(angle), width*0.5);
            glVertex3f(r1*cos(angle+3*da), r1*sin(angle+3*da), width*0.5);
      }
      glEnd();

      /* draw front sides of teeth */
      glBegin(GL_QUADS);
      da = 2.0*pi / teeth / 4.0;
      for (i=0;i<teeth;i++) {
            angle = i * 2.0*pi / teeth;
            glVertex3f(r1*cos(angle),      r1*sin(angle),      width*0.5);
            glVertex3f(r2*cos(angle+da),   r2*sin(angle+da),   width*0.5);
            glVertex3f(r2*cos(angle+2*da), r2*sin(angle+2*da), width*0.5);
            glVertex3f(r1*cos(angle+3*da), r1*sin(angle+3*da), width*0.5);
      }
      glEnd();

      glNormal3f(0.0, 0.0, -1.0);

      /* draw back face */
      glBegin(GL_QUAD_STRIP);
      for (i=0;i<=teeth;i++) {
            angle = i * 2.0*pi / teeth;
            glVertex3f(r1*cos(angle), r1*sin(angle), -width*0.5);
            glVertex3f(r0*cos(angle), r0*sin(angle), -width*0.5);
            glVertex3f(r1*cos(angle+3*da), r1*sin(angle+3*da), -width*0.5);
            glVertex3f(r0*cos(angle), r0*sin(angle), -width*0.5);
      }
      glEnd();

      /* draw back sides of teeth */
      glBegin(GL_QUADS);
      da = 2.0*pi / teeth / 4.0;
      for (i=0;i<teeth;i++) {
            angle = i * 2.0*pi / teeth;
            glVertex3f(r1*cos(angle+3*da), r1*sin(angle+3*da), -width*0.5);
            glVertex3f(r2*cos(angle+2*da), r2*sin(angle+2*da), -width*0.5);
            glVertex3f(r2*cos(angle+da),   r2*sin(angle+da),        -width*0.5);
            glVertex3f(r1*cos(angle),      r1*sin(angle),     -width*0.5);
      }
      glEnd();

      /* draw outward faces of teeth */
      glBegin(GL_QUAD_STRIP);
      for (i=0;i<teeth;i++) {
            angle = i * 2.0*pi / teeth;
            glVertex3f(r1*cos(angle),      r1*sin(angle),      width*0.5);
            glVertex3f(r1*cos(angle),      r1*sin(angle),     -width*0.5);
            u = r2*cos(angle+da) - r1*cos(angle);
            v = r2*sin(angle+da) - r1*sin(angle);
            len = sqrt(u*u + v*v);
            u /= len;
            v /= len;
            glNormal3f(v, -u, 0.0);
            glVertex3f(r2*cos(angle+da),   r2*sin(angle+da),         width*0.5);
            glVertex3f(r2*cos(angle+da),   r2*sin(angle+da),        -width*0.5);
            glNormal3f(cos(angle), sin(angle), 0.0);
            glVertex3f(r2*cos(angle+2*da), r2*sin(angle+2*da),  width*0.5);
            glVertex3f(r2*cos(angle+2*da), r2*sin(angle+2*da), -width*0.5);
            u = r1*cos(angle+3*da) - r2*cos(angle+2*da);
            v = r1*sin(angle+3*da) - r2*sin(angle+2*da);
            glNormal3f(v, -u, 0.0);
            glVertex3f(r1*cos(angle+3*da), r1*sin(angle+3*da),  width*0.5);
            glVertex3f(r1*cos(angle+3*da), r1*sin(angle+3*da), -width*0.5);
            glNormal3f(cos(angle), sin(angle), 0.0);
            }
      glVertex3f(r1*cos(0.0), r1*sin(0.0), width*0.5);
      glVertex3f(r1*cos(0.0), r1*sin(0.0), -width*0.5);
      glEnd();

      glShadeModel(GL_SMOOTH);

      /* draw inside radius cylinder */
      glBegin(GL_QUAD_STRIP);
      for (i=0;i<=teeth;i++) {
            angle = i * 2.0*pi / teeth;
            glNormal3f(-cos(angle), -sin(angle), 0.0);
            glVertex3f(r0*cos(angle), r0*sin(angle), -width*0.5);
            glVertex3f(r0*cos(angle), r0*sin(angle), width*0.5);
      }
      glEnd();
}



static void drawGearGL(int id)
{
      static GLfloat pos[4] = { 5.0f, 5.0f, 10.0f, 1.0f };
      static GLfloat ared[4] = { 0.8f, 0.1f, 0.0f, 1.0f };
      static GLfloat agreen[4] = { 0.0f, 0.8f, 0.2f, 1.0f };
      static GLfloat ablue[4] = { 0.2f, 0.2f, 1.0f, 1.0f };

      glLightfv(GL_LIGHT0, GL_POSITION, pos);
      glEnable(GL_CULL_FACE);
      glEnable(GL_LIGHTING);
      glEnable(GL_LIGHT0);
      glEnable(GL_DEPTH_TEST);

      switch (id)
      {
      case 1:
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ared);
            gearGL(1.0f, 4.0f, 1.0f, 20, 0.7f);
            break;
      case 2:
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, agreen);
            gearGL(0.5f, 2.0f, 2.0f, 10, 0.7f);
            break;
      case 3:
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ablue);
            gearGL(1.3f, 2.0f, 0.5f, 10, 0.7f);
            break;
      default:
            break;
      }
      glEnable(GL_NORMALIZE);
}


void RenderCamera()
{
    glRotatef(view_rotx, 1.0, 0.0, 0.0);
    glRotatef(view_roty, 0.0, 1.0, 0.0);
    glRotatef(view_rotz, 0.0, 0.0, 1.0);
}


void RenderScene()
{
      glPushMatrix();
      glTranslatef(-3.0, -2.0, 0.0);
      glRotatef(fAngle, 0.0, 0.0, 1.0);
      drawGearGL(1);
      glPopMatrix();

      glPushMatrix();
      glTranslatef(3.1f, -2.0f, 0.0f);
      glRotatef(-2.0 * fAngle - 9.0, 0.0, 0.0, 1.0);
      drawGearGL(2);
      glPopMatrix();

      glPushMatrix();
      glTranslatef(-3.1f, 2.2f, -1.8f);
      glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
      glRotatef(2.0 * fAngle - 2.0, 0.0, 0.0, 1.0);
      drawGearGL(3);
      glPopMatrix();
}


static void View(GHOST_IWindow* window, bool stereo, int eye = 0)
{
      window->activateDrawingContext();
      GHOST_Rect bnds;
      int noOfScanlines = 0, lowerScanline = 0;
      int verticalBlankingInterval = 32;  // hard coded for testing purposes, display device dependant
      float left, right, bottom, top;
      float nearplane, farplane, zeroPlane, distance;
      float eyeSeparation = 0.62f;
      window->getClientBounds(bnds);

      // viewport
      if(stereo)
      {
            if(nVidiaWindows)
            { 
                  // handled by nVidia driver so act as normal (explicitly put here since
                  // it -is- stereo)
                  glViewport(0, 0, bnds.getWidth(), bnds.getHeight());
            }
            else
            {  // generic cross platform above-below stereo
                  noOfScanlines = (bnds.getHeight() - verticalBlankingInterval) / 2;
                  switch(eye)
                  {
                        case LEFT_EYE:
                              // upper half of window
                              lowerScanline = bnds.getHeight() - noOfScanlines;
                              break;
                        case RIGHT_EYE:
                              // lower half of window
                              lowerScanline = 0;
                              break;
                  }
            }
      }
      else
      {
            noOfScanlines = bnds.getHeight();
            lowerScanline = 0;
      }

      glViewport(0, lowerScanline, bnds.getWidth(), noOfScanlines);

      // projection
      left = -6.0;
      right = 6.0;
      bottom = -4.8f;
      top = 4.8f;
      nearplane = 5.0;
      farplane = 60.0;

      if(stereo)
      {
            zeroPlane = 0.0;
            distance = 14.5;
            switch(eye)
            {
                  case LEFT_EYE:
                        StereoProjection(left, right, bottom, top, nearplane, farplane, zeroPlane, distance, -eyeSeparation / 2.0);
                        break;
                  case RIGHT_EYE:
                        StereoProjection(left, right, bottom, top, nearplane, farplane, zeroPlane, distance, eyeSeparation / 2.0);
                        break;
            }
      }
      else
      {
//          left = -w;
//          right = w;
//          bottom = -h;
//          top = h;
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glFrustum(left, right, bottom, top, 5.0, 60.0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glTranslatef(0.0, 0.0, -40.0);

      }

      glClearColor(.2f,0.0f,0.0f,0.0f);
}


void StereoProjection(float left, float right, float bottom, float top, float nearplane, float farplane,
            float zero_plane, float dist,
            float eye)
/* Perform the perspective projection for one eye's subfield.
The projection is in the direction of the negative z axis.

-6.0, 6.0, -4.8, 4.8,
left, right, bottom, top = the coordinate range, in the plane of zero
parallax setting, which will be displayed on the screen.  The
ratio between (right-left) and (top-bottom) should equal the aspect
ratio of the display.

6.0, -6.0,
near, far = the z-coordinate values of the clipping planes.

0.0,
zero_plane = the z-coordinate of the plane of zero parallax setting.

14.5,
dist = the distance from the center of projection to the plane
of zero parallax.

-0.31
eye = half the eye separation; positive for the right eye subfield,
negative for the left eye subfield.
*/
{
      float xmid, ymid, clip_near, clip_far, topw, bottomw, leftw, rightw,
      dx, dy, n_over_d;

      dx = right - left;
      dy = top - bottom;

      xmid = (right + left) / 2.0;
      ymid = (top + bottom) / 2.0;

      clip_near = dist + zero_plane - nearplane;
      clip_far  = dist + zero_plane - farplane;

      n_over_d = clip_near / dist;

      topw = n_over_d * dy / 2.0;
      bottomw = -topw;
      rightw = n_over_d * (dx / 2.0 - eye);
      leftw  = n_over_d *(-dx / 2.0 - eye);

      /* Need to be in projection mode for this. */
      glLoadIdentity();
      glFrustum(leftw,  rightw,  bottomw,  topw,  clip_near,  clip_far);

      glTranslatef(-xmid - eye,  -ymid,  -zero_plane - dist);
      return;
} /* stereoproj */


class Application : public GHOST_IEventConsumer {
public:
      Application(GHOST_ISystem* system);
      ~Application(void);
      virtual     bool processEvent(GHOST_IEvent* event);

      GHOST_ISystem* m_system;
      GHOST_IWindow* m_mainWindow;
      GHOST_IWindow* m_secondaryWindow;
      GHOST_IWindow* m_fullScreenWindow;
      GHOST_ITimerTask* m_gearsTimer, *m_testTimer;
      GHOST_TStandardCursor m_cursor;
      bool m_exitRequested;

      bool stereo;
};


Application::Application(GHOST_ISystem* system)
      : m_system(system), m_mainWindow(0), m_secondaryWindow(0), m_fullScreenWindow(0),
        m_gearsTimer(0), m_testTimer(0), m_cursor(GHOST_kStandardCursorFirstCursor),
        m_exitRequested(false), stereo(false)
{
      fApp = this;

      // Create the main window
      STR_String title1 ("gears - main window");
      m_mainWindow = system->createWindow(title1, 10, 64, 320, 200, GHOST_kWindowStateNormal,
            GHOST_kDrawingContextTypeOpenGL, true /* stereo flag */);

    if (!m_mainWindow) {
            std::cout << "could not create main window\n";
            exit(-1);
    }

      // Create a secondary window
      STR_String title2 ("gears - secondary window");
      m_secondaryWindow = system->createWindow(title2, 340, 64, 320, 200, GHOST_kWindowStateNormal,
            GHOST_kDrawingContextTypeOpenGL, false /* stereo flag */);
      if (!m_secondaryWindow) {
            cout << "could not create secondary window\n";
            exit(-1);
      }

      // Install a timer to have the gears running
      m_gearsTimer = system->installTimer(0 /*delay*/, 20/*interval*/, gearsTimerProc, m_mainWindow);
}


Application::~Application(void)
{
      // Dispose windows
      if (m_system->validWindow(m_mainWindow)) {
            m_system->disposeWindow(m_mainWindow);
      }
      if (m_system->validWindow(m_secondaryWindow)) {
            m_system->disposeWindow(m_secondaryWindow);
      }
}


bool Application::processEvent(GHOST_IEvent* event)
{
      GHOST_IWindow* window = event->getWindow();
      bool handled = true;

      switch (event->getType()) {
/*    case GHOST_kEventUnknown:
            break;
      case GHOST_kEventCursorButton:
            std::cout << "GHOST_kEventCursorButton"; break;
      case GHOST_kEventCursorMove:
            std::cout << "GHOST_kEventCursorMove"; break;
*/
      case GHOST_kEventWheel:
            {
            GHOST_TEventWheelData* wheelData = (GHOST_TEventWheelData*) event->getData();
            if (wheelData->z > 0)
            {
                  view_rotz += 5.f;
            }
            else
            {
                  view_rotz -= 5.f;
            }
            }
            break;

      case GHOST_kEventKeyUp:
            break;

      case GHOST_kEventKeyDown:
            {
            GHOST_TEventKeyData* keyData = (GHOST_TEventKeyData*) event->getData();
            switch (keyData->key) {
            case GHOST_kKeyC:
                  {
                  int cursor = m_cursor;
                  cursor++;
                  if (cursor >= GHOST_kStandardCursorNumCursors) {
                        cursor = GHOST_kStandardCursorFirstCursor;
                  }
                  m_cursor = (GHOST_TStandardCursor)cursor;
                  window->setCursorShape(m_cursor);
                  }
                  break;

            case GHOST_kKeyE:
                  {
                  int x = 200, y= 200;
                  m_system->setCursorPosition(x,y);
                  break;
                  }

            case GHOST_kKeyF:
                  if (!m_system->getFullScreen()) {
                        // Begin fullscreen mode
                        GHOST_DisplaySetting setting;
                        
                        setting.bpp = 16;
                        setting.frequency = 50;
                        setting.xPixels = 640;
                        setting.yPixels = 480;
                        m_system->beginFullScreen(setting, &m_fullScreenWindow, false /* stereo flag */);
                  }
                  else {
                        m_system->endFullScreen();
                        m_fullScreenWindow = 0;
                  }
                  break;

            case GHOST_kKeyH:
                  window->setCursorVisibility(!window->getCursorVisibility());
                  break;

            case GHOST_kKeyM:
                  {
                        bool down = false;
                        m_system->getModifierKeyState(GHOST_kModifierKeyLeftShift,down);
                        if (down) {
                              std::cout << "left shift down\n";
                        }
                        m_system->getModifierKeyState(GHOST_kModifierKeyRightShift,down);
                        if (down) {
                              std::cout << "right shift down\n";                                                                                                                                                                    }
                        m_system->getModifierKeyState(GHOST_kModifierKeyLeftAlt,down);
                        if (down) { 
                              std::cout << "left Alt down\n";
                        }
                        m_system->getModifierKeyState(GHOST_kModifierKeyRightAlt,down);
                        if (down) {
                              std::cout << "right Alt down\n";
                        }
                        m_system->getModifierKeyState(GHOST_kModifierKeyLeftControl,down);
                        if (down) {
                              std::cout << "left control down\n";
                        }
                        m_system->getModifierKeyState(GHOST_kModifierKeyRightControl,down);
                        if (down) {
                              std::cout << "right control down\n";
                        }
                  }
                  break;

            case GHOST_kKeyQ:
                  if (m_system->getFullScreen())
                  {
                        m_system->endFullScreen();
                        m_fullScreenWindow = 0;
                  }
                  m_exitRequested = true;
                  break;

            case GHOST_kKeyS:  // toggle mono and stereo
                  if(stereo)
                        stereo = false;
                  else
                        stereo = true;
                  break;

            case GHOST_kKeyT:
                  if (!m_testTimer) {
                        m_testTimer = m_system->installTimer(0, 1000, testTimerProc);
                  }

                  else {
                        m_system->removeTimer(m_testTimer);
                        m_testTimer = 0;
                  }

                  break;

            case GHOST_kKeyW:
                  if (m_mainWindow)
                  {
                        STR_String title;
                        m_mainWindow->getTitle(title);
                        title += "-";
                        m_mainWindow->setTitle(title);

                  }
                  break;

            default:
                  break;
            }
            }
            break;

      case GHOST_kEventWindowClose:
            {
            GHOST_IWindow* window2 = event->getWindow();
            if (window2 == m_mainWindow) {
                  m_exitRequested = true;
            }
            else {
                  m_system->disposeWindow(window2);
            }
            }
            break;

      case GHOST_kEventWindowActivate:
            handled = false;
            break;

      case GHOST_kEventWindowDeactivate:
            handled = false;
            break;

      case GHOST_kEventWindowUpdate:
            {
                  GHOST_IWindow* window2 = event->getWindow();
                  if(!m_system->validWindow(window2))
                        break;

                  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

                  if(stereo)
                  {
                        View(window2, stereo, LEFT_EYE);
                        glPushMatrix();
                        RenderCamera();
                        RenderScene();
                        glPopMatrix();

                        View(window2, stereo, RIGHT_EYE);
                        glPushMatrix();
                        RenderCamera();
                        RenderScene();
                        glPopMatrix();
                  }
                  else
                  {
                        View(window2, stereo);
                        glPushMatrix();
                        RenderCamera();
                        RenderScene();
                        glPopMatrix();
                  }
                  window2->swapBuffers();
            }
            break;
            
      default:
            handled = false;
            break;
      }
      return handled;
}


int main(int /*argc*/, char** /*argv*/)
{
      nVidiaWindows = false;
//    nVidiaWindows = true;

#ifdef WIN32
      /* Set a couple of settings in the registry for the nVidia detonator driver.
       * So this is very specific...
       */
      if(nVidiaWindows)
      {
            LONG lresult;
            HKEY hkey = 0;
            DWORD dwd = 0;
            //unsigned char buffer[128];

            CRegKey regkey;
            //DWORD keyValue;
//          lresult = regkey.Open(HKEY_LOCAL_MACHINE, "SOFTWARE\\NVIDIA Corporation\\Global\\Stereo3D\\StereoEnable");
            lresult = regkey.Open(HKEY_LOCAL_MACHINE, "SOFTWARE\\NVIDIA Corporation\\Global\\Stereo3D\\StereoEnable",
                   KEY_ALL_ACCESS );

            if(lresult == ERROR_SUCCESS)
                  printf("Succesfully opened key\n");
#if 0
            lresult = regkey.QueryValue(&keyValue, "StereoEnable");
            if(lresult == ERROR_SUCCESS)
                  printf("Succesfully queried key\n");
#endif
            lresult = regkey.SetValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\NVIDIA Corporation\\Global\\Stereo3D\\StereoEnable",
                        "1");
            if(lresult == ERROR_SUCCESS)
                  printf("Succesfully set value for key\n");
            regkey.Close();
            if(lresult == ERROR_SUCCESS)
                  printf("Succesfully closed key\n");
//          regkey.Write("2");
      }
#endif  // WIN32

      // Create the system
      GHOST_ISystem::createSystem();
      fSystem = GHOST_ISystem::getSystem();

      if (fSystem) {
            // Create an application object
            Application app (fSystem);

            // Add the application as event consumer
            fSystem->addEventConsumer(&app);
                
            // Enter main loop
            while (!app.m_exitRequested) {
            //printf("main: loop\n");
                  fSystem->processEvents(true);
                  fSystem->dispatchEvents();
            }
      }

      // Dispose the system
      GHOST_ISystem::disposeSystem();

      return 0;
}


static void gearsTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 /*time*/)
{
    fAngle += 2.0;
    view_roty += 1.0;
      GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
      if (fApp->m_fullScreenWindow) {
            // Running full screen
            fApp->m_fullScreenWindow->invalidate();
      }
      else {
            if (fSystem->validWindow(window)) {
                  window->invalidate();
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index