Login  Register

NEWTCanvasJFX Resize

Posted by MikeORC on Jan 10, 2020; 3:19pm
URL: https://forum.jogamp.org/NEWTCanvasJFX-Resize-tp4040254.html

Hi,

I am experimenting with NEWTCanvasJFX and see promising results however, I cannot get it to resize within a JavaFX window.  I have a stackpane which I add the NEWTCanvasJFX as a child in the controller initialization code.  The size for the NEWTCanvasJFX has to be set programmatically but the resize does not work.  The reshape method does not get called.

Here is the code:
package sample;

import com.jogamp.newt.javafx.NewtCanvasJFX;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.GL4;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLJPanel;
import com.jogamp.opengl.util.FPSAnimator;

import org.joml.Matrix4f;

import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ResourceBundle;
import java.util.Scanner;

import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import static com.jogamp.opengl.GL.GL_ARRAY_BUFFER;
import static com.jogamp.opengl.GL.GL_BACK;
import static com.jogamp.opengl.GL.GL_BLEND;
import static com.jogamp.opengl.GL.GL_CCW;
import static com.jogamp.opengl.GL.GL_COLOR_BUFFER_BIT;
import static com.jogamp.opengl.GL.GL_CULL_FACE;
import static com.jogamp.opengl.GL.GL_DEPTH_BUFFER_BIT;
import static com.jogamp.opengl.GL.GL_DEPTH_TEST;
import static com.jogamp.opengl.GL.GL_ELEMENT_ARRAY_BUFFER;
import static com.jogamp.opengl.GL.GL_FLOAT;
import static com.jogamp.opengl.GL.GL_LEQUAL;
import static com.jogamp.opengl.GL.GL_SCISSOR_TEST;
import static com.jogamp.opengl.GL.GL_STATIC_DRAW;
import static com.jogamp.opengl.GL.GL_STENCIL_TEST;
import static com.jogamp.opengl.GL.GL_TRIANGLES;
import static com.jogamp.opengl.GL.GL_TRUE;
import static com.jogamp.opengl.GL.GL_UNSIGNED_SHORT;
import static com.jogamp.opengl.GL2ES2.GL_COMPILE_STATUS;
import static com.jogamp.opengl.GL2ES2.GL_FRAGMENT_SHADER;
import static com.jogamp.opengl.GL2ES2.GL_LINK_STATUS;
import static com.jogamp.opengl.GL2ES2.GL_VERTEX_SHADER;

public class Controller extends Application implements Initializable, GLEventListener
{
  @FXML
  private StackPane openGLPane;
  private long previousTime;
  private long frameCount;
  private int[] buffers;
  private int program;
  private float aspectRatio;
  private final float[] matrixArray = new float[16];
  private float angle;
  private NewtCanvasJFX glPanel;
  private boolean useExperimental = true;

  @Override
  public void start(Stage primaryStage) throws Exception
  {
    Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
    primaryStage.setTitle("JavaFX OpenGL");
    primaryStage.setScene(new Scene(root, 300, 275));
    primaryStage.show();
  }

  public static void main(String[] args)
  {
    launch(args);
  }

  @Override
  public void initialize(URL location, ResourceBundle resources)
  {
    final GLProfile glProfile = GLProfile.getDefault();
    final GLCapabilities capabilities = new GLCapabilities(glProfile);

    if (useExperimental)
    {
      GLWindow glWindow = GLWindow.create(capabilities);

      glPanel = new NewtCanvasJFX(glWindow);
      glPanel.setWidth(300);
      glPanel.setHeight(300);
      openGLPane.getChildren().add(glPanel);

      System.out.println("openglpane resizable: " + openGLPane.isResizable());
      System.out.println("glPanel resizable: " + glPanel.isResizable());

      glWindow.addGLEventListener(this);

      final FPSAnimator animator = new FPSAnimator(glWindow, -1);
      animator.start();
    }
    else
    {
      final GLJPanel glPanel = new GLJPanel(capabilities);
      glPanel.addGLEventListener(this);

      final SwingNode swingNode = new SwingNode();
      swingNode.setContent(glPanel);
      openGLPane.getChildren().add(swingNode);

      final FPSAnimator animator = new FPSAnimator(glPanel, -1);
      animator.start();
    }
  }

  @Override
  public void init(GLAutoDrawable glAutoDrawable)
  {
    final GL4 gl = glAutoDrawable.getGL().getGL4();

    this.buffers = new int[2];
    gl.glGenBuffers(this.buffers.length, this.buffers, 0);

    final FloatBuffer positions =
        ByteBuffer.allocateDirect(8 * 3 * Float.BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();

    positions.put(.5f).put(.5f).put(.5f);    // 0
    positions.put(-.5f).put(.5f).put(.5f);   // 1
    positions.put(-.5f).put(-.5f).put(.5f);  // 2
    positions.put(.5f).put(-.5f).put(.5f);   // 3
    positions.put(.5f).put(.5f).put(-.5f);   // 4
    positions.put(-.5f).put(.5f).put(-.5f);  // 5
    positions.put(-.5f).put(-.5f).put(-.5f); // 6
    positions.put(.5f).put(-.5f).put(-.5f);  // 7

    positions.rewind();

    gl.glBindBuffer(GL_ARRAY_BUFFER, this.buffers[0]);
    gl.glBufferData(GL_ARRAY_BUFFER, positions.capacity() * Float.BYTES, positions, GL_STATIC_DRAW);

    final ShortBuffer indices =
        ByteBuffer.allocateDirect(12 * 3 * Short.BYTES).order(ByteOrder.nativeOrder()).asShortBuffer();

    indices.put((short) 0).put((short) 1).put((short) 2);
    indices.put((short) 0).put((short) 2).put((short) 3);
    indices.put((short) 4).put((short) 0).put((short) 3);
    indices.put((short) 4).put((short) 3).put((short) 7);
    indices.put((short) 4).put((short) 5).put((short) 1);
    indices.put((short) 4).put((short) 1).put((short) 0);
    indices.put((short) 1).put((short) 5).put((short) 6);
    indices.put((short) 1).put((short) 6).put((short) 2);
    indices.put((short) 3).put((short) 2).put((short) 6);
    indices.put((short) 3).put((short) 6).put((short) 7);
    indices.put((short) 5).put((short) 4).put((short) 7);
    indices.put((short) 5).put((short) 7).put((short) 6);

    indices.rewind();

    gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.buffers[1]);
    gl.glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.capacity() * Short.BYTES, indices, GL_STATIC_DRAW);

    final int vertexShader = compileShader(gl, GL_VERTEX_SHADER, loadCode("vertex.glsl"));
    final int fragmentShader = compileShader(gl, GL_FRAGMENT_SHADER, loadCode("fragment.glsl"));
    this.program = compileProgram(gl, vertexShader, fragmentShader);
  }

  private String loadCode(String resourceName)
  {
    try (final Scanner stream = new Scanner(this.getClass().getResourceAsStream("/" + resourceName)))
    {
      stream.useDelimiter("\\Z");

      return stream.next();
    }
  }

  @Override
  public void dispose(GLAutoDrawable glAutoDrawable)
  {

  }

  @Override
  public void display(GLAutoDrawable glAutoDrawable)
  {
    final long now = System.nanoTime();
    final long deltaTime = now - this.previousTime;
    ++this.frameCount;

    if (deltaTime >= 1_000_000_000L)
    {
      System.out.println("FPS: " + this.frameCount / (deltaTime / 1_000_000_000.0));
      this.frameCount = 0;
      this.previousTime = now;
    }

    final GL4 gl = glAutoDrawable.getGL().getGL4();

    gl.glEnable(GL_DEPTH_TEST);
    gl.glDepthFunc(GL_LEQUAL);
    gl.glDepthMask(true);
    gl.glClearDepthf(1);

    gl.glEnable(GL_CULL_FACE);
    gl.glCullFace(GL_BACK);
    gl.glFrontFace(GL_CCW);

    gl.glDisable(GL_BLEND);
    gl.glDisable(GL_SCISSOR_TEST);
    gl.glDisable(GL_STENCIL_TEST);

    gl.glColorMask(true, true, true, true);
    gl.glClearColor(0, 0, 0, 1);
    gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    gl.glUseProgram(this.program);

    this.angle += Math.toRadians(1);

    new Matrix4f().setPerspective((float) Math.toRadians(60), this.aspectRatio, 0, 1000).mul(
        new Matrix4f().setLookAt(0, 0, 3, 0, 0, 0, 0, 1, 0)).mul(
        new Matrix4f().setRotationXYZ(this.angle, this.angle, 0)).get(this.matrixArray);

    gl.glUniformMatrix4fv(gl.glGetUniformLocation(program, "MVP"), 1, false, this.matrixArray, 0);
    gl.glUniform4f(gl.glGetUniformLocation(this.program, "color"), 0, 1, 0, 1);

    final int positionAttribute = gl.glGetAttribLocation(this.program, "position");

    gl.glEnableVertexAttribArray(positionAttribute);
    gl.glBindBuffer(GL_ARRAY_BUFFER, this.buffers[0]);
    gl.glVertexAttribPointer(positionAttribute, 3, GL_FLOAT, false, 0, 0);

    gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.buffers[1]);
    gl.glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
  }

  @Override
  public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height)
  {
    final GL4 gl = glAutoDrawable.getGL().getGL4();

    gl.glViewport(0, 0, width, height);
    this.aspectRatio = width / (float) height;
    System.out.println("reshape: " + width + " " + height);

    //glPanel.resize(width, height);
    if (glPanel != null)
    {
      glPanel.setWidth(width);
      glPanel.setHeight(height);
    }
  }

  private int compileShader(final GL4 gl, final int type, final String code)
  {
    int shader = gl.glCreateShader(type);

    if (shader != 0)
    {
      // add the source code to the shader and compile it
      gl.glShaderSource(shader, 1, new String[] {code}, new int[] {code.length()}, 0);
      gl.glCompileShader(shader);

      String statusString = "";
      int[] status = new int[1];

      gl.glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);

      if (status[0] != GL_TRUE)
      {
        int[] length = new int[1];
        byte[] statusBytes = new byte[1024];
        gl.glGetShaderInfoLog(shader, statusBytes.length, length, 0, statusBytes, 0);
        statusString = new String(statusBytes);
      }

      if (!statusString.isEmpty())
      {
        gl.glDeleteShader(shader);
        throw new RuntimeException("Could not compile shader: " + statusString + "\n" + code);
      }
    }

    return shader;
  }

  private int compileProgram(final GL4 gl, final int vertexShader, final int fragmentShader)
  {
    int programId = gl.glCreateProgram();

    if (programId != 0)
    {
      gl.glAttachShader(programId, vertexShader);
      gl.glAttachShader(programId, fragmentShader);

      gl.glLinkProgram(programId);

      gl.glUseProgram(programId);

      String programStatus = "";
      int[] status = new int[1];

      gl.glGetProgramiv(programId, GL_LINK_STATUS, status, 0);

      if (status[0] != GL_TRUE)
      {
        int[] length = new int[1];
        byte[] statusBytes = new byte[1024];
        gl.glGetShaderInfoLog(programId, statusBytes.length, length, 0, statusBytes, 0);
        programStatus = new String(statusBytes);
      }

      if (!programStatus.isEmpty())
      {
        gl.glDeleteProgram(programId);
        throw new RuntimeException("Could not link program: " + programStatus);
      }
    }

    return programId;
  }
}

FXML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1024.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
    <children>
        <MenuBar>
            <menus>
                <Menu mnemonicParsing="false" text="File">
                    <items>
                        <MenuItem mnemonicParsing="false" text="Close" />
                    </items>
                </Menu>
                <Menu mnemonicParsing="false" text="Edit">
                    <items>
                        <MenuItem mnemonicParsing="false" text="Delete" />
                    </items>
                </Menu>
                <Menu mnemonicParsing="false" text="Help">
                    <items>
                        <MenuItem mnemonicParsing="false" text="About" />
                    </items>
                </Menu>
            </menus>
        </MenuBar>
        <StackPane fx:id="openGLPane" VBox.vgrow="ALWAYS" />
    </children>
</VBox>