OZ++ Class: OpenGLView
/******************************************************************************
 *
 * Copyright (c) 2015 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *  OpenGLView.h
 *
 *****************************************************************************/

//2015/08/26 Added save, saveASJPG, saveASPNG methods.
//2016/09/17 Added new Contructor takes as parent parameter of OpenGLForm.
#pragma once

#include <oz++/Exception.h>
#include <oz++/motif/Image.h>
#include <oz++/motif/Canvas.h>
#include <oz++/opengl/OpenGLVisualBuffer.h>
#include <oz++/opengl/OpenGLVisual.h>
#include <oz++/opengl/OpenGLContext.h>
#include <oz++/opengl/OpenGLBitmap.h>
#include <oz++/opengl/OpenGLMainView.h>
#include <oz++/opengl/OpenGLForm.h>
#include <oz++/opengl/OpenGLImageInfo.h>
#include <oz++/opengl/OpenGLBulletinBoard.h>

namespace OZ {

class OpenGLView: public Canvas {
private:
  OpenGLContext*   context;
  int              bufferType;
    
public:
  //Please redefine a virtual display method in your own class derived from this OpenView class.
  virtual void display()
  {
     printf("OpenGLView::display\n");
  }

  virtual void expose(Action& action)
  {
    setViewPort();
    //Call a virtual display method to display OpenGL figures.
    display();
      
    glFlush();

    if (bufferType == OpenGL_DOUBLE_BUFFER) {
      swapBuffers();
    }
  }

  void setViewPort()
  {
    Dimension w, h;
    get(XmNwidth, (XtArgVal)&w);
    get(XmNheight, (XtArgVal)&h);
       
    //printf("OpenGLView::setViewPort w=%d h=%d\n", w, h);
    glViewport(0, 0, w, h);
  }

  //2017/03/01
  void setPerspective(GLdouble fovy=16.0,
        GLdouble zNear=0.5,
        GLdouble zFar =100.0f)
  {
     //Set a proportional perspective.
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     int w = width();
     int h = height();
     if (h > 0) {
       GLdouble aspect = (GLdouble)w / (GLdouble)h;
       gluPerspective(fovy, aspect, zNear, zFar);
     }
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
  }

  //If needed, please redefine a virtual intialize method in your own class derived from this Canvas class.
  virtual void initialize()
  {
     //printf("OpenGLView::initialize\n");
  }

  void mapNofity(Event& event)
  {
    //printf("OpenGLView::mapNotify\n");
    Window window = this->getWindow();
    context -> makeCurrent(window);
    setViewPort();
      
    Dimension width, height;
    get(XmNwidth, (XtArgVal)&width);
    get(XmNheight, (XtArgVal)&height);

    resize(width, height);

    //Call a virtual initialize method       
    initialize();
  }

  virtual void resize(int width, int height)
  {
  }
    
  void configureNotify(Event& event)
  {
    //printf("OpenGLView::ConfigureNotify\n");
    setViewPort();
    //Call resize
      
    Dimension width, height;
    get(XmNwidth, (XtArgVal)&width);
    get(XmNheight, (XtArgVal)&height);
    //Call a virtual resize method
    resize(width, height);
  }

  void swapBuffers()
  {
    glXSwapBuffers(getDisplay(), getWindow());
  }
    
public:
  OpenGLView(OpenGLMainView* parent, const char* name, Args& args)
  :Canvas(parent->getMainWindow(), name,// args),
                        args.set(XmNdepth, (XtArgVal)parent->getDepth())
                            .set(XmNvisual, (XtArgVal)parent->getVisual())
                            .set(XmNcolormap, (XtArgVal)parent->getColormap()) ),
       
  context(NULL)
  {
    //Shallow copy of a openGLContext from the parent.
    context =  parent->getOpenGLContext(); 

    bufferType = parent->getBufferType();

    if (args.has(XmNopenGLBufferType)) {
      bufferType = (int)args.get(XmNopenGLBufferType);
    }
  }

  OpenGLView(OpenGLForm* parent, const char* name, Args& args)
  :Canvas(parent, name, args.set(XmNdepth, (XtArgVal)parent->getDepth())
                            .set(XmNvisual, (XtArgVal)parent->getVisual())
                            .set(XmNcolormap, (XtArgVal)parent->getColormap()) ),

  context(NULL)
  {
    //Shallow copy of a openGLContext from the parent.
    context =  parent->getOpenGLContext();

    bufferType = parent->getBufferType();

    if (args.has(XmNopenGLBufferType)) {
      bufferType = (int)args.get(XmNopenGLBufferType);
    }
  }

  //2017/09/02
  OpenGLView(OpenGLBulletinBoard* parent, const char* name, Args& args)
  :Canvas(parent, name, args.set(XmNdepth, (XtArgVal)parent->getDepth())
                            .set(XmNvisual, (XtArgVal)parent->getVisual())
                            .set(XmNcolormap, (XtArgVal)parent->getColormap()) ),

  context(NULL)
  {
    //Shallow copy of a openGLContext from the parent.
    context =  parent->getOpenGLContext();

    bufferType = parent->getBufferType();

    if (args.has(XmNopenGLBufferType)) {
      bufferType = (int)args.get(XmNopenGLBufferType);
    }
  }

  ~OpenGLView()
  {
    context = NULL;
  }
    
  OpenGLContext* getOpenGLContext()
  {
    return context;
  }

  //2015/08/22
  virtual void save(const char* filename)
  {
    if (StringT<char>::endsWithIgnoreCase(filename, ".jpg")) {
      saveAsJPG(filename);
    } 
    else if (StringT<char>::endsWithIgnoreCase(filename, ".png")) {
      saveAsPNG(filename);
    } else {
        //Ignore
    }
  }

  virtual void saveAsJPG(const char* filename)
  {
    try {
      Dimension width = 0;
      Dimension height = 0;
      get(XmNwidth,  (XtArgVal)&width);
      get(XmNheight, (XtArgVal)&height);
      int rw = (width / 8) * 8;
          
      OpenGLBitmap bitmap(0, 0, rw, height, 24, GL_RGB);

      bitmap.read(GL_FRONT);
      bitmap.saveAsJPG(filename);
     printf("saveAsJPG %s\n", filename);
    } catch (Exception& ex) {
      ex.display();
    }      
  }

  virtual void saveAsPNG(const char* filename)
  {
    try {
      Dimension width = 0;
      Dimension height = 0;
      get(XmNwidth,  (XtArgVal)&width);
      get(XmNheight, (XtArgVal)&height);
      int rw = (width / 8) * 8;
          
      OpenGLBitmap bitmap(0, 0, width, height, 8, GL_RGBA);
      bitmap.read(GL_FRONT);
      bitmap.saveAsPNG(filename);
     printf("saveAsPNG %s\n", filename);
    } catch (Exception& ex) {
      ex.display();
    }      
  }

  void refresh()
  {
    unmap();
    map();
  }

};

}